b2evolution PHP Cross Reference Blogging Systems

Source: /inc/items/model/_item.funcs.php - 4126 lines - 129238 bytes - Summary - Text - Print

Description: This file implements Post handling functions. This file is part of the evoCore framework - {@link http://evocore.net/} See also {@link http://sourceforge.net/projects/evocms/}.

   1  <?php
   2  /**
   3   * This file implements Post handling functions.
   4   *
   5   * This file is part of the evoCore framework - {@link http://evocore.net/}
   6   * See also {@link http://sourceforge.net/projects/evocms/}.
   7   *
   8   * @copyright (c)2003-2014 by Francois Planque - {@link http://fplanque.com/}
   9   * Parts of this file are copyright (c)2004-2006 by Daniel HAHLER - {@link http://thequod.de/contact}.
  10   *
  11   * {@internal License choice
  12   * - If you have received this file as part of a package, please find the license.txt file in
  13   *   the same folder or the closest folder above for complete license terms.
  14   * - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
  15   *   then you must choose one of the following licenses before using the file:
  16   *   - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
  17   *   - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
  18   * }}
  19   *
  20   * {@internal Open Source relicensing agreement:
  21   * Daniel HAHLER grants Francois PLANQUE the right to license
  22   * Daniel HAHLER's contributions to this file and the b2evolution project
  23   * under any OSI approved OSS license (http://www.opensource.org/licenses/).
  24   * }}
  25   *
  26   * @package evocore
  27   *
  28   * {@internal Below is a list of authors who have contributed to design/coding of this file: }}
  29   * @author cafelog (team)
  30   * @author blueyed: Daniel HAHLER.
  31   * @author fplanque: Francois PLANQUE.
  32   * @author tswicegood: Travis SWICEGOOD.
  33   * @author vegarg: Vegar BERG GULDAL.
  34   *
  35   * @version $Id: _item.funcs.php 6136 2014-03-08 07:59:48Z manuel $
  36   */
  37  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  38  
  39  load_class( 'items/model/_itemlight.class.php', 'ItemLight' );
  40  load_class( 'items/model/_itemlist.class.php', 'ItemList2' );
  41  
  42  /**
  43   * Prepare the MainList object for displaying skins.
  44   *
  45   * @param integer max # of posts on the page
  46   */
  47  function init_MainList( $items_nb_limit )
  48  {
  49      global $MainList, $Blog, $Plugins;
  50      global $preview;
  51      global $disp;
  52      global $postIDlist, $postIDarray;
  53  
  54      // Allow plugins to prepare their own MainList object
  55      if( ! $Plugins->trigger_event_first_true('InitMainList', array( 'MainList' => &$MainList, 'limit' => $items_nb_limit ) ) )
  56      {
  57          $MainList = new ItemList2( $Blog, $Blog->get_timestamp_min(), $Blog->get_timestamp_max(), $items_nb_limit );    // COPY (FUNC)
  58  
  59          if( ! $preview )
  60          {
  61              if( $disp == 'page' )
  62              {    // Get pages:
  63                  $MainList->set_default_filters( array(
  64                          'types' => '1000',        // pages
  65                      ) );
  66              }
  67  
  68              // else: we are in posts mode
  69  
  70              // pre_dump( $MainList->default_filters );
  71              $MainList->load_from_Request( false );
  72              // pre_dump( $MainList->filters );
  73              // echo '<br/>'.( $MainList->is_filtered() ? 'filtered' : 'NOT filtered' );
  74              // $MainList->dump_active_filters();
  75  
  76              // Run the query:
  77              $MainList->query();
  78  
  79              // Old style globals for category.funcs:
  80              $postIDlist = $MainList->get_page_ID_list();
  81              $postIDarray = $MainList->get_page_ID_array();
  82          }
  83          else
  84          {    // We want to preview a single post, we are going to fake a lot of things...
  85              $MainList->preview_from_request();
  86  
  87              // Legacy for the category display
  88              $cat_array = array();
  89          }
  90      }
  91  
  92      param( 'more', 'integer', 0, true );
  93      param( 'page', 'integer', 1, true ); // Post page to show
  94      param( 'c',    'integer', 0, true ); // Display comments?
  95      param( 'tb',   'integer', 0, true ); // Display trackbacks?
  96      param( 'pb',   'integer', 0, true ); // Display pingbacks?
  97  }
  98  
  99  
 100  /**
 101   * Prepare the 'In-skin editing'.
 102   *
 103   */
 104  function init_inskin_editing()
 105  {
 106      global $Blog, $edited_Item, $action, $form_action;
 107      global $item_tags, $item_title, $item_content;
 108      global $admin_url, $redirect_to, $advanced_edit_link;
 109  
 110      if( ! $Blog->get_setting( 'in_skin_editing' ) )
 111      {    // Redirect to the Back-office editing (setting is OFF)
 112          header_redirect( $admin_url.'?ctrl=items&action=new&blog='.$Blog->ID );
 113      }
 114  
 115      $tab_switch_params = 'blog='.$Blog->ID;
 116  
 117      // Post ID, go from $_GET when we edit post from Front-office
 118      $post_ID = param( 'p', 'integer', 0 );
 119  
 120      // Post ID, go from $_GET when we copy post from Front-office
 121      $copy_post_ID = param( 'cp', 'integer', 0 );
 122  
 123      if( $post_ID > 0 )
 124      {    // Edit post
 125          global $post_extracats;
 126          $action = 'edit';
 127  
 128          $ItemCache = & get_ItemCache ();
 129          $edited_Item = $ItemCache->get_by_ID ( $post_ID );
 130  
 131          check_categories_nosave ( $post_category, $post_extracats );
 132          $post_extracats = postcats_get_byID( $post_ID );
 133  
 134          $redirect_to = url_add_param( $admin_url, 'ctrl=items&filter=restore&blog='.$Blog->ID.'&highlight='.$edited_Item->ID, '&' );
 135          $tab_switch_params .= '&amp;p='.$edited_Item->ID;
 136      }
 137      elseif( $copy_post_ID > 0 )
 138      {    // Copy post
 139          global $localtimenow;
 140          $action = 'new';
 141  
 142          $ItemCache = & get_ItemCache ();
 143          $edited_Item = $ItemCache->get_by_ID ( $copy_post_ID );
 144  
 145          $edited_Item_Blog = $edited_Item->get_Blog();
 146          $item_status = $edited_Item_Blog->get_allowed_item_status();
 147  
 148          $edited_Item->set( 'status', $item_status );
 149          $edited_Item->set( 'dateset', 0 );    // Date not explicitly set yet
 150          $edited_Item->set( 'issue_date', date( 'Y-m-d H:i:s', $localtimenow ) );
 151  
 152          modules_call_method( 'constructor_item', array( 'Item' => & $edited_Item ) );
 153  
 154          check_categories_nosave ( $post_category, $post_extracats );
 155  
 156          $redirect_to = url_add_param( $admin_url, 'ctrl=items&filter=restore&blog='.$Blog->ID, '&' );
 157      }
 158      elseif( empty( $action ) )
 159      {    // Create new post (from Front-office)
 160          $action = 'new';
 161  
 162          load_class( 'items/model/_item.class.php', 'Item' );
 163          $edited_Item = new Item();
 164          $def_status = get_highest_publish_status( 'post', $Blog->ID, false );
 165          $edited_Item->set( 'status', $def_status );
 166          check_categories_nosave ( $post_category, $post_extracats );
 167          $edited_Item->set('main_cat_ID', $Blog->get_default_cat_ID());
 168  
 169          // Set default locations from current user
 170          $edited_Item->set_creator_location( 'country' );
 171          $edited_Item->set_creator_location( 'region' );
 172          $edited_Item->set_creator_location( 'subregion' );
 173          $edited_Item->set_creator_location( 'city' );
 174  
 175          // Set object params:
 176          $edited_Item->load_from_Request( /* editing? */ false, /* creating? */ true );
 177  
 178          $redirect_to = url_add_param( $admin_url, 'ctrl=items&filter=restore&blog='.$Blog->ID, '&' );
 179      }
 180  
 181      // Used in the edit form:
 182  
 183      // We never allow HTML in titles, so we always encode and decode special chars.
 184      $item_title = htmlspecialchars_decode( $edited_Item->title );
 185  
 186      if( $Blog->get_setting( 'allow_html_post' ) )
 187      {    // HTML is allowed for this post, we have HTML in the DB and we can edit it:
 188          $item_content = $edited_Item->content;
 189      }
 190      else
 191      {    // HTML is disallowed for this post, content is encoded in DB and we need to decode it for editing:
 192          $item_content = htmlspecialchars_decode( $edited_Item->content );
 193      }
 194  
 195      // Format content for editing, if we were not already in editing...
 196      $Plugins_admin = & get_Plugins_admin();
 197      $edited_Item->load_Blog();
 198      $params = array( 'object_type' => 'Item', 'object_Blog' => & $edited_Item->Blog );
 199      $Plugins_admin->unfilter_contents( $item_title /* by ref */, $item_content /* by ref */, $edited_Item->get_renderers_validated(), $params );
 200  
 201      $item_tags = implode( ', ', $edited_Item->get_tags() );
 202  
 203      // Get an url for a link 'Go to advanced edit screen'
 204      $mode_editing = param( 'mode_editing', 'string', 'expert' );
 205      $entries = get_item_edit_modes( $Blog->ID, $action, $admin_url, $tab_switch_params );
 206      $advanced_edit_link = $entries[$mode_editing];
 207  
 208      $form_action = get_samedomain_htsrv_url().'item_edit.php';
 209  }
 210  
 211  /**
 212   * Return an Item if an Intro or a Featured item is available for display in current disp.
 213   *
 214   * @return Item
 215   */
 216  function & get_featured_Item()
 217  {
 218      global $Blog;
 219      global $disp, $disp_detail, $MainList, $FeaturedList;
 220      global $featured_displayed_item_IDs;
 221  
 222      if( $disp != 'posts' || !isset($MainList) )
 223      {    // If we're not currently displaying posts, no need to try & display a featured/intro post on top!
 224          $Item = NULL;
 225          return $Item;
 226      }
 227  
 228      if( !isset( $FeaturedList ) )
 229      {    // Don't repeat if we've done this already -- Initialize the featured list only first time this function is called in a skin:
 230  
 231          // Get ready to obtain 1 post only:
 232          $FeaturedList = new ItemList2( $Blog, $Blog->get_timestamp_min(), $Blog->get_timestamp_max(), 1 );
 233  
 234          // Set default filters for the current page:
 235          $FeaturedList->set_default_filters( $MainList->filters );
 236  
 237          if( ! $MainList->is_filtered() )
 238          {    // This is not a filtered page, so we are on the home page.
 239              // The competing intro-* types are: 'main' and 'all':
 240              // fplanque> IMPORTANT> nobody changes this without consulting the manual and talking to me first!
 241              $restrict_to_types = '1500,1600';
 242          }
 243          else
 244          {    // We are on a filtered... it means a category page or sth like this...
 245              // echo $disp_detail;
 246              switch( $disp_detail )
 247              {
 248                  case 'posts-cat':
 249                  case 'posts-subcat':
 250                      // The competing intro-* types are: 'cat' and 'all':
 251                      // fplanque> IMPORTANT> nobody changes this without consulting the manual and talking to me first!
 252                      $restrict_to_types = '1520,1600';
 253                      break;
 254  
 255                  case 'posts-tag':
 256                      // The competing intro-* types are: 'tag' and 'all':
 257                      // fplanque> IMPORTANT> nobody changes this without consulting the manual and talking to me first!
 258                      $restrict_to_types = '1530,1600';
 259                      break;
 260  
 261                  default:
 262                      // The competing intro-* types are: 'sub' and 'all':
 263                      // fplanque> IMPORTANT> nobody changes this without consulting the manual and talking to me first!
 264                      $restrict_to_types = '1570,1600';
 265              }
 266          }
 267  
 268          $FeaturedList->set_filters( array(
 269                  'types' => $restrict_to_types,
 270              ), false /* Do NOT memorize!! */ );
 271          // pre_dump( $FeaturedList->filters );
 272          // Run the query:
 273          $FeaturedList->query();
 274  
 275          if( $FeaturedList->result_num_rows == 0 )
 276          { // No Intro page was found, try to find a featured post instead:
 277  
 278              $FeaturedList->reset();
 279  
 280              $FeaturedList->set_filters( array(
 281                      'featured' => 1,  // Featured posts only (TODO!)
 282                      // Types will already be reset to defaults here
 283                  ), false /* Do NOT memorize!! */ );
 284  
 285              // Run the query:
 286              $FeaturedList->query();
 287          }
 288      }
 289  
 290      // Get next featured item
 291      $Item = $FeaturedList->get_item();
 292  
 293      if( $Item )
 294      {    // Memorize that ID so that it can later be filtered out normal display:
 295          $featured_displayed_item_IDs[] = $Item->ID;
 296      }
 297  
 298      return $Item;
 299  }
 300  
 301  
 302  /**
 303   * Get item type ID by type code
 304   *
 305   * @param string Type code
 306   * @return integer Item type ID
 307   */
 308  function get_item_type_ID( $type_code )
 309  {
 310      $item_types = array(
 311              1    => 'post',
 312              1000 => 'page',
 313              1500 => 'intro-main',
 314              1520 => 'intro-cat',
 315              1530 => 'intro-tag',
 316              1570 => 'intro-sub',
 317              1600 => 'intro-all',
 318              2000 => 'podcast',
 319              3000 => 'sidebar-link',
 320              4000 => 'advertisement',
 321              //5000 => 'reserved',
 322          );
 323  
 324      $item_type_ID = array_search( $type_code, $item_types );
 325  
 326      if( $item_type_ID === false )
 327      { // No found type, Use standard type ID = 1
 328          $item_type_ID = 1;
 329      }
 330  
 331      return $item_type_ID;
 332  }
 333  
 334  
 335  /**
 336   * Validate URL title (slug) / Also used for category slugs
 337   *
 338   * Using title as a source if url title is empty.
 339   * We allow up to 200 chars (which is ridiculously long) for WP import compatibility.
 340   * New slugs will be cropped to 5 words so the URLs are not too long.
 341   *
 342   * @param string url title to validate
 343   * @param string real title to use as a source if $urltitle is empty (encoded in $evo_charset)
 344   * @param integer ID of post
 345   * @param boolean Query the DB, but don't modify the URL title if the title already exists (Useful if you only want to alert the pro user without making changes for him)
 346   * @param string The prefix of the database column names (e. g. "post_" for post_urltitle)
 347   * @param string The name of the post ID column
 348   * @param string The name of the DB table to use
 349   * @param NULL|string The post locale or NULL if there is no specific locale.
 350   * @return string validated url title
 351   */
 352  function urltitle_validate( $urltitle, $title, $post_ID = 0, $query_only = false,
 353                                      $dbSlugFieldName = 'post_urltitle', $dbIDname = 'post_ID',
 354                                      $dbtable = 'T_items__item', $post_locale = NULL )
 355  {
 356      global $DB, $Messages;
 357  
 358      $urltitle = trim( $urltitle );
 359      $orig_title = $urltitle;
 360  
 361      if( empty( $urltitle ) )
 362      {
 363          if( ! empty($title) )
 364              $urltitle = $title;
 365          else
 366              $urltitle = 'title';
 367      }
 368  
 369      // echo 'starting with: '.$urltitle.'<br />';
 370  
 371      // Replace special chars/umlauts, if we can convert charsets:
 372      load_funcs('locales/_charset.funcs.php');
 373      $urltitle = replace_special_chars($urltitle, $post_locale);
 374  
 375      // Make everything lowercase and use trim again after replace_special_chars
 376      $urltitle = strtolower( trim ( $urltitle ) );
 377  
 378      if( empty( $urltitle ) )
 379      {
 380          $urltitle = 'title';
 381      }
 382  
 383      // Leave only first 5 words in order to get a shorter URL
 384      // (which is generally accepted as a better practice)
 385      // User can manually enter a very long URL if he wants
 386      $slug_changed = param( 'slug_changed' );
 387      if( $slug_changed == 0 )
 388      { // this should only happen when the slug is auto generated
 389          global $Blog;
 390          if( isset( $Blog ) )
 391          { // Get max length of slug from current blog setting
 392              $count_of_words = $Blog->get_setting('slug_limit');
 393          }
 394          if( empty( $count_of_words ) )
 395          { // Use 5 words to limit slug by default
 396              $count_of_words = 5;
 397          }
 398  
 399          $title_words = array();
 400          $title_words = explode( '-', $urltitle );
 401          if( count($title_words) > $count_of_words )
 402          {
 403              $urltitle = '';
 404              for( $i = 0; $i < $count_of_words; $i++ )
 405              {
 406                  $urltitle .= $title_words[$i].'-';
 407              }
 408              //delete last '-'
 409              $urltitle = substr( $urltitle, 0, strlen($urltitle) - 1 );
 410          }
 411  
 412          // echo 'leaving 5 words: '.$urltitle.'<br />';
 413      }
 414  
 415      // Normalize to 200 chars + a number
 416      preg_match( '/^(.*?)((-|_)+([0-9]+))?$/', $urltitle, $matches );
 417      $urlbase = substr( $matches[1], 0, 200 );
 418      // strip a possible dash at the end of the URL title:
 419      $urlbase = rtrim( $urlbase, '-' );
 420      $urltitle = $urlbase;
 421      if( ! empty( $matches[4] ) )
 422      {
 423          $urltitle .= '-'.$matches[4];
 424      }
 425  
 426      if( !$query_only )
 427      {
 428          // TODO: dh> this might get used to utilize the SlugCache instead of the processing below.
 429          #if( $post_ID && $dbtable == 'T_slug' )
 430          #{
 431          #    $existing_Slug = get_SlugCache()->get_by_name($urltitle, false, false);
 432          #    if( $existing_Slug )
 433          #    {
 434          #        $slug_field_name = preg_replace('~^slug_~', '', $dbIDname);
 435          #        if( $existing_Slug->get($slug_field_name) == $urltitle )
 436          #        {
 437          #            // OK
 438          #        }
 439          #    }
 440          #}
 441          // CHECK FOR UNIQUENESS:
 442          // Find all occurrences of urltitle-number in the DB:
 443          $sql = 'SELECT '.$dbSlugFieldName.', '.$dbIDname.'
 444                            FROM '.$dbtable.'
 445                           WHERE '.$dbSlugFieldName." REGEXP '^".$urlbase."(-[0-9]+)?$'";
 446          $exact_match = false;
 447          $highest_number = 0;
 448          $use_existing_number = NULL;
 449          foreach( $DB->get_results( $sql, ARRAY_A ) as $row )
 450          {
 451              $existing_urltitle = $row[$dbSlugFieldName];
 452              // echo "existing = $existing_urltitle <br />";
 453              if( $existing_urltitle == $urltitle && $row[$dbIDname] != $post_ID )
 454              { // We have an exact match, we'll have to change the number.
 455                  $exact_match = true;
 456              }
 457              if( preg_match( '/-([0-9]+)$/', $existing_urltitle, $matches ) )
 458              { // This one has a number, we extract it:
 459                  $existing_number = (int)$matches[1];
 460  
 461                  if( ! isset($use_existing_number) && $row[$dbIDname] == $post_ID )
 462                  { // if there is a numbered entry for the current ID, use this:
 463                      $use_existing_number = $existing_number;
 464                  }
 465  
 466                  if( $existing_number > $highest_number )
 467                  { // This is the new high
 468                      $highest_number = $existing_number;
 469                  }
 470              }
 471          }
 472          // echo "highest existing number = $highest_number <br />";
 473  
 474          if( $exact_match && !$query_only )
 475          { // We got an exact (existing) match, we need to change the number:
 476              $number = $use_existing_number ? $use_existing_number : ($highest_number+1);
 477              $urltitle = $urlbase.'-'.$number;
 478          }
 479      }
 480  
 481      // echo "using = $urltitle <br />";
 482  
 483      if( !empty($orig_title) && $urltitle != $orig_title )
 484      {
 485          $Messages->add( sprintf(T_('Warning: the URL slug has been changed to &laquo;%s&raquo;.'), $urltitle ), 'note' );
 486      }
 487  
 488      return $urltitle;
 489  }
 490  
 491  
 492  /**
 493   * if global $postdata was not set it will be
 494   */
 495  function get_postdata($postid)
 496  {
 497      global $DB, $postdata, $show_statuses;
 498  
 499      if( !empty($postdata) && $postdata['ID'] == $postid )
 500      { // We are asking for postdata of current post in memory! (we're in the b2 loop)
 501          // Already in memory! This will be the case when generating permalink at display
 502          // (but not when sending trackbacks!)
 503          // echo "*** Accessing post data in memory! ***<br />\n";
 504          return($postdata);
 505      }
 506  
 507      // echo "*** Loading post data! ***<br>\n";
 508      // We have to load the post
 509      $sql = 'SELECT post_ID, post_creator_user_ID, post_datestart, post_datemodified, post_status, post_content, post_title,
 510                                              post_main_cat_ID, cat_blog_ID ';
 511      $sql .= ', post_locale, post_url, post_wordcount, post_comment_status, post_views ';
 512      $sql .= '    FROM T_items__item
 513                       INNER JOIN T_categories ON post_main_cat_ID = cat_ID
 514                       WHERE post_ID = '.$postid;
 515      // Restrict to the statuses we want to show:
 516      // echo $show_statuses;
 517      // fplanque: 2004-04-04: this should not be needed here. (and is indeed problematic when we want to
 518      // get a post before even knowning which blog it belongs to. We can think of putting a security check
 519      // back into the Item class)
 520      // $sql .= ' AND '.statuses_where_clause( $show_statuses );
 521  
 522      // echo $sql;
 523  
 524      if( $myrow = $DB->get_row( $sql ) )
 525      {
 526          $mypostdata = array (
 527              'ID' => $myrow->post_ID,
 528              'Author_ID' => $myrow->post_creator_user_ID,
 529              'Date' => $myrow->post_datestart,
 530              'Status' => $myrow->post_status,
 531              'Content' => $myrow->post_content,
 532              'Title' => $myrow->post_title,
 533              'Category' => $myrow->post_main_cat_ID,
 534              'Locale' => $myrow->post_locale,
 535              'Url' => $myrow->post_url,
 536              'Wordcount' => $myrow->post_wordcount,
 537              'views' => $myrow->post_views,
 538              'comment_status' => $myrow->post_comment_status,
 539              'Blog' => $myrow->cat_blog_ID,
 540              );
 541  
 542          // Caching is particularly useful when displaying a single post and you call single_post_title several times
 543          if( !isset( $postdata ) ) $postdata = $mypostdata;    // Will save time, next time :)
 544  
 545          return($mypostdata);
 546      }
 547  
 548      return false;
 549  }
 550  
 551  
 552  
 553  
 554  
 555  // @@@ These aren't template tags, do not edit them
 556  
 557  
 558  /**
 559   * Returns the number of the words in a string, sans HTML
 560   *
 561   * @todo dh> Test if http://de3.php.net/manual/en/function.str-word-count.php#85579 works better/faster
 562   *           (only one preg_* call and no loop).
 563   *
 564   * @param string The string.
 565   * @return integer Number of words.
 566   *
 567   * @internal PHP's str_word_count() is not accurate. Inaccuracy reported
 568   *           by sam2kb: http://forums.b2evolution.net/viewtopic.php?t=16596
 569   */
 570  function bpost_count_words( $str )
 571  {
 572      global $evo_charset;
 573  
 574      $str = trim( strip_tags( $str ) );
 575  
 576      // Note: The \p escape sequence is available since PHP 4.4.0 and 5.1.0.
 577      if( @preg_match( '|\pL|u', 'foo' ) === false )
 578      {
 579          return str_word_count( $str );
 580      }
 581  
 582      $count = 0;
 583  
 584      foreach( preg_split( '#\s+#', convert_charset( $str, 'UTF-8', $evo_charset ), -1,
 585                              PREG_SPLIT_NO_EMPTY ) as $word )
 586      {
 587          if( preg_match( '#\pL#u', $word ) )
 588          {
 589              ++$count;
 590          }
 591      }
 592  
 593      return $count;
 594  }
 595  
 596  
 597  /**
 598   * Get allowed statuses for the current User
 599   *
 600   * Note: This function must be called only from the statuses_where_clause()!
 601   *
 602   * @param array statuses to filter
 603   * @param string database status field column prefix
 604   * @param integer the blog ID where to check allowed statuses
 605   * @param string the status permission prefix
 606   * @return string the condition
 607   */
 608  function get_allowed_statuses_condition( $statuses, $dbprefix, $req_blog, $perm_prefix )
 609  {
 610      global $current_User;
 611  
 612      // init where clauses array
 613      $where = array();
 614      // init allowed statuses array
 615      $allowed_statuses = array();
 616  
 617      $is_logged_in = is_logged_in( false );
 618      $creator_coll_name = ( $dbprefix == 'post_' ) ? $dbprefix.'creator_user_ID' : $dbprefix.'author_ID';
 619      // Iterate through all statuses and set allowed to true only if the corresponding status is allowed in case of any post/comments
 620      // If the status is not allowed to show, but exists further conditions which may allow it, then set the condition.
 621      foreach( $statuses as $key => $status )
 622      {
 623          switch( $status )
 624          {
 625              case 'published': // Published post/comments are always allowed
 626                  $allowed = true;
 627                  break;
 628  
 629              case 'community': // It is always allowed for logged in users
 630                  $allowed = $is_logged_in;
 631                  break;
 632  
 633              case 'protected': // It is always allowed for members
 634                  $allowed = ( $is_logged_in && ( $current_User->check_perm( 'blog_ismember', 1, false, $req_blog ) ) );
 635                  break;
 636  
 637              case 'private': // It is allowed for users who has global 'editall' permission but only on back-office
 638                  $allowed = ( is_admin_page() && $current_User->check_perm( 'blogs', 'editall' ) );
 639                  if( !$allowed && $is_logged_in && $current_User->check_perm( $perm_prefix.'private', 'create', false, $req_blog ) )
 640                  { // Own private posts/comments are allowed if user can create private posts/comments
 641                      $where[] = ' ( '.$dbprefix."status = 'private' AND ".$creator_coll_name.' = '.$current_User->ID.' ) ';
 642                  }
 643                  break;
 644  
 645              case 'review': // It is allowed for users who have permission to create comments with 'review' status and have at least 'lt' posts/comments edit perm
 646                  $allowed = ( $is_logged_in && $current_User->check_perm( $perm_prefix.'review', 'moderate', false, $req_blog ) );
 647                  if( !$allowed && $is_logged_in && $current_User->check_perm( $perm_prefix.'review', 'create', false, $req_blog ) )
 648                  { // Own posts/comments with 'review' status are allowed if user can create posts/comments with 'review' status
 649                      $where[] = ' ( '.$dbprefix."status = 'review' AND ".$creator_coll_name.' = '.$current_User->ID.' ) ';
 650                  }
 651                  break;
 652  
 653              case 'draft': // In back-office it is always allowed for users who may create posts/commetns with 'draft' status
 654                  $allowed = ( is_admin_page() && $current_User->check_perm( $perm_prefix.'draft', 'create', false, $req_blog ) );
 655                  if( !$allowed && $is_logged_in && $current_User->check_perm( $perm_prefix.'draft', 'create', false, $req_blog ) )
 656                  { // In front-office only authors may see their own draft posts/comments, but only if the have permission to create draft posts/comments
 657                      $where[] = ' ( '.$dbprefix."status = 'draft' AND ".$creator_coll_name.' = '.$current_User->ID.' ) ';
 658                  }
 659                  break;
 660  
 661              case 'deprecated': // In back-office it is always allowed for users who may create posts/comments with 'deprecated' status
 662                  $allowed = ( is_admin_page() && $current_User->check_perm( $perm_prefix.'deprecated', 'create', false, $req_blog ) );
 663                  // In front-office it is never allowed
 664                  break;
 665  
 666              case 'redirected': // In back-office it is always allowed for users who may create posts/comments with 'deprecated' status
 667                  $allowed = ( is_admin_page() && $current_User->check_perm( $perm_prefix.'redirected', 'create', false, $req_blog ) );
 668                  // In front-office it is never allowed
 669                  break;
 670  
 671              case 'trash':
 672                  // Currently only users with global editall permissions are allowed to view/delete recycled comments
 673                  $allowed = ( ( $dbprefix == 'comment_' ) && is_admin_page() && $current_User->check_perm( 'blogs', 'editall' ) );
 674                  // In front-office it is never allowed
 675                  break;
 676  
 677              default: // Allow other statuses are restricted. It is very important to keep this restricted because of SQL injections also, so we never allow a status what we don't know.
 678                  $allowed = false;
 679          }
 680  
 681          if( $allowed )
 682          { // All posts/comments with this status can be displayed in the request blog for the current User ( or for anonymous )
 683              $allowed_statuses[] = $status;
 684          }
 685      }
 686  
 687      if( count( $allowed_statuses ) )
 688      { // add allowed statuses condition
 689          $where[] = $dbprefix.'status IN ( \''.implode( '\',\'', $allowed_statuses ).'\' )';
 690      }
 691  
 692      // Implode conditions collected in the $where array
 693      // NOTE: If the array is empty, it means that the user has no permission to the requested statuses, so FALSE must be returned
 694      $where_condition = count( $where ) > 0 ? ' ( '.implode( ' OR ', $where ).' ) ' : ' FALSE ';
 695      return $where_condition;
 696  }
 697  
 698  
 699  /**
 700   * Construct the where clause to limit retrieved posts/comment on their status
 701   *
 702   * TODO: asimo> would be good to move this function to an items and comments common file
 703   *
 704   * @param Array statuses of posts/comments we want to get
 705   * @param string post/comment table db prefix
 706   * @param integer blog ID
 707   * @param string permission prefix: 'blog_post!' or 'blog_comment!'
 708   * @param boolean filter statuses by the current user perm and current page, by default is true. It should be false only e.g. when we have to count comments awaiting moderation.
 709   * @return string statuses where condition
 710   */
 711  function statuses_where_clause( $show_statuses = NULL, $dbprefix = 'post_', $req_blog = NULL, $perm_prefix = 'blog_post!', $filter_by_perm = true, $author_filter = NULL )
 712  {
 713      global $current_User, $blog, $DB;
 714  
 715      if( is_null( $req_blog ) )
 716      { // try to init request blog if it was not set
 717          global $blog;
 718          $req_blog = $blog;
 719      }
 720  
 721      if( empty( $show_statuses ) )
 722      { // use in-skin statuses if show_statuses is empty
 723          $show_statuses = get_inskin_statuses();
 724      }
 725  
 726      // init where clauses array
 727      $where = array();
 728  
 729      // Check modules item statuses where condition but only in case of the items, and don't need to check in case of comments
 730      if( $req_blog && ( $perm_prefix == 'blog_post!' ) )
 731      { // If requested blog is set, then set additional "where" clauses from modules method, before we would manipulate the $show_statuses array
 732          $modules_condition = modules_call_method( 'get_item_statuses_where_clause', array( 'blog_ID' => $req_blog, 'statuses' => $show_statuses ) );
 733          if( !empty( $modules_condition ) )
 734          {
 735              foreach( $modules_condition as $condition )
 736              {
 737                  if( ! empty( $condition ) )
 738                  { // condition is not empty
 739                      $where[] = $condition;
 740                  }
 741              }
 742          }
 743      }
 744  
 745      if( is_logged_in( false ) )
 746      { // User is logged in and the account was activated
 747          if( $current_User->check_perm( 'blogs', 'editall', false ) )
 748          { // User has permission to all blogs posts and comments, we don't have to check blog specific permissions.
 749              $allowed_statuses_cond = get_allowed_statuses_condition( $show_statuses, $dbprefix, NULL, $perm_prefix );
 750              if( ! empty( $allowed_statuses_cond ) )
 751              { // condition is not empty
 752                  $where[] = $allowed_statuses_cond;
 753              }
 754              $filter_by_perm = false;
 755              $show_statuses = NULL;
 756          }
 757          elseif( !empty( $author_filter ) && ( $author_filter != $current_User->ID ) )
 758          { // Author filter is set, but current_User is not the filtered user, then these statuses are not visible for sure
 759              $show_statuses = array_diff( $show_statuses, array( 'private', 'draft' ) );
 760          }
 761      }
 762  
 763      if( ( $req_blog == 0 ) && $filter_by_perm )
 764      { // This is a very special case when we must check visibility statuses from each blog separately
 765          // Note: This case should not be called frequently, it may be really slow!!
 766          $where_condition = '';
 767          $condition = '';
 768          if( in_array( 'published', $show_statuses ) )
 769          { // 'published' status is always allowed in case of all blogs, handle this condition separately
 770              $where_condition = '( '.$dbprefix.'status = "published" )';
 771              $condition = ' OR ';
 772          }
 773          if( !is_logged_in( false ) )
 774          { // When user is not logged in only the 'published' status is allowed, don't check more
 775              return $where_condition;
 776          }
 777          if( in_array( 'community', $show_statuses ) )
 778          { // 'community' status is always allowed in case of all blogs when a user is logged in, handle this condition separately
 779              $where_condition .= $condition.'( '.$dbprefix.'status = "community" )';
 780              $condition = ' OR ';
 781          }
 782          // Remove 'published' and 'community' statuses because those were already handled
 783          $show_statuses = array_diff( $show_statuses, array( 'published', 'community' ) );
 784          if( empty( $show_statuses ) )
 785          { // return if there are no other status
 786              return $where_condition;
 787          }
 788          // Select each blog
 789          $blog_ids = $DB->get_col( 'SELECT blog_ID FROM T_blogs' );
 790          $sub_condition = '';
 791          foreach( $blog_ids as $blog_id )
 792          { // create statuses where clause condition for each blog separately
 793              $status_perm = statuses_where_clause( $show_statuses, $dbprefix, $blog_id, $perm_prefix, $filter_by_perm, $author_filter );
 794              if( $status_perm )
 795              { // User has permission to view some statuses on this blog
 796                  $sub_condition .= '( ( cat_blog_ID = '.$blog_id.' ) AND'.$status_perm.' ) OR ';
 797              }
 798          }
 799          if( $dbprefix == 'post_' )
 800          { // Item query condition
 801              $from_table = 'FROM T_items__item';
 802              $reference_column = 'post_ID';
 803          }
 804          else
 805          { // Comment query condition
 806              $from_table = 'FROM T_comments';
 807              $reference_column ='comment_post_ID';
 808          }
 809          // Select each object ID which corresponds to the statuses condition.
 810          // Note: This is a very slow query when there is many post/comments. This case should not be used frequently.
 811          $sub_query = 'SELECT '.$dbprefix.'ID '.$from_table.'
 812                          INNER JOIN T_postcats ON '.$reference_column.' = postcat_post_ID
 813                          INNER JOIN T_categories ON postcat_cat_ID = cat_ID
 814                          WHERE ('.substr( $sub_condition, 0, ( strlen( $sub_condition ) - 3 ) ).')';
 815          $object_ids = implode( ',', $DB->get_col( $sub_query ) );
 816          if( $object_ids )
 817          { // If thre is at least one post or comment
 818              $where_condition .= $condition.'( '.$dbprefix.'ID IN ('.$object_ids.') )';
 819          }
 820          return $where_condition;
 821      }
 822  
 823      if( $filter_by_perm )
 824      { // filter allowed statuses by the current User perms and by the current page ( front or back office )
 825          $allowed_statuses_cond = get_allowed_statuses_condition( $show_statuses, $dbprefix, $req_blog, $perm_prefix );
 826          if( ! empty( $allowed_statuses_cond ) )
 827          { // condition is not empty
 828              $where[] = $allowed_statuses_cond;
 829          }
 830      }
 831      elseif( count( $show_statuses ) )
 832      { // we are not filtering so all status are allowed, add allowed statuses condition
 833          $where[] = $dbprefix.'status IN ( \''.implode( '\',\'', $show_statuses ).'\' )';
 834      }
 835  
 836      $where = count( $where ) > 0 ? ' ( '.implode( ' OR ', $where ).' ) ' : '';
 837  
 838      // echo $where;
 839      return $where;
 840  }
 841  
 842  
 843  /**
 844   * Get post category setting
 845   *
 846   * @param int blog id
 847   * @return int setting value
 848   */
 849  function get_post_cat_setting( $blog )
 850  {
 851      $BlogCache = & get_BlogCache();
 852      $Blog = $BlogCache->get_by_ID( $blog, false, false );
 853      if( ! $Blog )
 854      {
 855          return -1;
 856      }
 857      $post_categories = $Blog->get_setting( 'post_categories' );
 858      switch( $post_categories )
 859      {
 860          case 'no_cat_post':
 861              return 0;
 862          case 'one_cat_post':
 863              return 1;
 864          case 'multiple_cat_post':
 865              return 2;
 866          case 'main_extra_cat_post':
 867              return 3;
 868      }
 869  }
 870  
 871  /**
 872   * Recreate posts autogenerated excerpts
 873   * Only those posts excerpt will be recreated which are visible in the front office
 874   *
 875   * Note: This process can be very very slow if there are many items in the database
 876   *
 877   * @param string the url to call if the process needs to be paused becuase of the max execution time
 878   * @param boolean set true to start from the beginning, false otherwise
 879   * @param boolean set to true to display recreated/all information or leave it on false to display only dots
 880   */
 881  function recreate_autogenerated_excerpts( $continue_url, $remove_all = true, $detailed_progress_log = false )
 882  {
 883      global $DB, $localtimenow;
 884  
 885      $start_time = time();
 886      $max_exec_time = ini_get( 'max_execution_time' );
 887      $load_limit = 50;
 888      $progress_log_id = 'progress_log';
 889      // The timestamp when the process was started
 890      $process_start_ts = param( 'start_ts', 'string', $localtimenow );
 891  
 892      // Update only those posts which may be visible in the front office
 893      $where_condition = '( post_excerpt_autogenerated = 1 OR post_excerpt_autogenerated IS NULL ) AND post_status IN ( \''.implode( '\',\' ', get_inskin_statuses( NULL, 'post' ) ).'\' )';
 894      // Update only those posts which were already created when the recreate process was started
 895      $where_condition .= ' AND post_datecreated < '.$DB->quote( date2mysql( $process_start_ts ) );
 896  
 897      if( $remove_all )
 898      { // We are in the beginning of the process and first we set all autogenerated excerpts values to NULL
 899          if( $detailed_progress_log )
 900          { // Display detailed progess information
 901              echo '<span id="'.$progress_log_id.'">'.T_('Clearing autogenerated excerpt values').'</span>';
 902              evo_flush();
 903          }
 904  
 905          // Set all autogenerated excerpt values to NULL
 906          $query = 'UPDATE T_items__item SET post_excerpt = NULL WHERE '.$where_condition;
 907          $DB->query( $query, 'Remove all autogenerated excerpts' );
 908  
 909          // Count all autogenerated excerpt which value is NULL ( Note: Maybe some of them was already NULL before we have started this process )
 910          $all_excerpt = $DB->get_var( 'SELECT count(*) FROM T_items__item WHERE post_excerpt IS NULL AND '.$where_condition );
 911          $recreated_excerpts = 0;
 912      }
 913      else
 914      { // Init params with a previously started process status
 915          echo '<span id="progress_log"></span>';
 916          $all_excerpt = param( 'all_excerpt', 'integer', 0 );
 917          $recreated_excerpts = param( 'recreated_excerpts', 'integer', 0 );
 918      }
 919  
 920      // Display the current state of the process or a '.' character to indicate the ongoing process
 921      if( $detailed_progress_log )
 922      {
 923          echo_progress_log_update( $progress_log_id, $recreated_excerpts, $all_excerpt );
 924      }
 925      else
 926      {
 927          echo ' .';
 928      }
 929      evo_flush();
 930  
 931      $load_SQL = new SQL();
 932      $load_SQL->SELECT( '*' );
 933      $load_SQL->FROM( 'T_items__item' );
 934      $load_SQL->WHERE( $where_condition.' AND post_excerpt IS NULL' );
 935      $load_SQL->LIMIT( $load_limit );
 936  
 937      $ItemCache = & get_ItemCache();
 938      while( count( $ItemCache->load_by_sql( $load_SQL ) ) > 0 )
 939      { // New portion of items was loaded
 940          $processed_items = 0;
 941          while( ( $iterator_Item = & $ItemCache->get_next() ) != NULL )
 942          { // Create new autogenerated excerpt and save it in the database
 943              $excerpt = $iterator_Item->get_autogenerated_excerpt();
 944              // Update excerpt without Item->dbupdate() call to make it faster
 945              $DB->query( 'UPDATE T_items__item SET post_excerpt = '.$DB->quote( $excerpt ).' WHERE post_ID = '.$DB->quote( $iterator_Item->ID ) );
 946              $processed_items++;
 947              if( $detailed_progress_log && ( ( $processed_items % 3 ) == 0 ) )
 948              { // Update progress info after every 3 recreated excerpt when detailed progress log was requested
 949                  echo_progress_log_update( $progress_log_id, $recreated_excerpts + $processed_items, $all_excerpt );
 950                  evo_flush();
 951              }
 952          }
 953  
 954          // Increase the number recreated excerpts
 955          $recreated_excerpts += $processed_items;
 956  
 957          // Clear already process items from the cache
 958          $ItemCache->clear();
 959          // Display progress log
 960          if( $detailed_progress_log )
 961          {
 962              echo_progress_log_update( $progress_log_id, $recreated_excerpts, $all_excerpt );
 963          }
 964          else
 965          {
 966              echo ' .';
 967          }
 968          evo_flush();
 969  
 970          if( ( $max_exec_time != 0 ) && ( $recreated_excerpts < $all_excerpt ) )
 971          { // a max execution time limit is set and there are more excerpts to create
 972              $elapsed_time = time() - $start_time;
 973              if( $elapsed_time > ( $max_exec_time - 10 ) )
 974              { // Increase the time limit in case if less than 10 seconds remained
 975                  $max_exec_time = $max_exec_time + 100;
 976                  if( ! set_max_execution_time( $max_exec_time ) )
 977                  {
 978                      $continue_url = url_add_param( $continue_url, 'all_excerpt='.$all_excerpt.'&amp;recreated_excerpts='.$recreated_excerpts.'&amp;start_ts='.$process_start_ts, '&amp;' );
 979                      echo '<br />'.'We are reaching the time limit for this script. Please click <a href="'.$continue_url.'">continue</a>...';
 980                      evo_flush();
 981                      exit(0);
 982                  }
 983              }
 984          }
 985      }
 986  
 987      // Check if the recreated exceprts number match with the total number of autogenerated excerpts
 988      if( $detailed_progress_log && ( $recreated_excerpts < $all_excerpt ) )
 989      { // This means that we are in the end of the process but some post excerpt was updated outside of this process
 990          // Here we increase the recreated excerpts value because all excerpts were recreated, however some of them not during this process
 991          echo_progress_log_update( $progress_log_id, $all_excerpt, $all_excerpt );
 992          if( $all_excerpt == ( $recreated_excerpts + 1 ) )
 993          {
 994              echo '<br />'.T_('Note: One excerpt was re-created in another simultaneous process!');
 995          }
 996          else
 997          {
 998              echo '<br />'.sprintf( T_('Note: %d excerpts were re-created in another simultaneous process!'), $all_excerpt - $recreated_excerpts );
 999          }
1000      }
1001  }
1002  
1003  
1004  /**
1005   * Creates a link to new category, with properties icon
1006   *
1007   * @return string link url
1008   */
1009  function get_newcategory_link()
1010  {
1011      global $dispatcher, $blog;
1012      $new_url = $dispatcher.'?ctrl=chapters&amp;action=new&amp;blog='.$blog;
1013      $link = ' <span class="floatright">'.action_icon( T_('Add new category'), 'new', $new_url, '', 5, 1 ).'</span>';
1014      return $link;
1015  }
1016  
1017  /**
1018   * Allow recursive category selection.
1019   *
1020   * @todo Allow to use a dropdown (select) to switch between blogs ( CSS / JS onchange - no submit.. )
1021   *
1022   * @param Form
1023   * @param boolean true: use form fields, false: display only
1024   * @param boolean true: show links for add new category & manual
1025   * @param array Params
1026   */
1027  function cat_select( $Form, $form_fields = true, $show_title_links = true, $params = array() )
1028  {
1029      global $cache_categories, $blog, $current_blog_ID, $current_User, $edited_Item, $cat_select_form_fields;
1030      global $cat_sel_total_count, $dispatcher;
1031      global $rsc_url;
1032  
1033      if( get_post_cat_setting( $blog ) < 1 )
1034      { // No categories for $blog
1035          return;
1036      }
1037  
1038      $params = array_merge( array(
1039              'categories_name' => T_('Categories'),
1040          ), $params );
1041  
1042      $cat = param( 'cat', 'integer', 0 );
1043      if( empty( $edited_Item->ID ) && !empty( $cat ) )
1044      {    // If the GET param 'cat' is defined we should preselect the category for new created post
1045          global $post_extracats;
1046          $post_extracats = array( $cat );
1047          $edited_Item->main_cat_ID = $cat;
1048      }
1049  
1050      if( $show_title_links )
1051      {    // Use in Back-office
1052          $fieldset_title = get_newcategory_link().$params['categories_name'].get_manual_link('item_categories_fieldset');
1053      }
1054      else
1055      {
1056          $fieldset_title = $params['categories_name'];
1057      }
1058  
1059      $Form->begin_fieldset( $fieldset_title, array( 'class'=>'extracats', 'id' => 'itemform_categories' ) );
1060  
1061      $cat_sel_total_count = 0;
1062      $r ='';
1063  
1064      $cat_select_form_fields = $form_fields;
1065  
1066      cat_load_cache(); // make sure the caches are loaded
1067  
1068      $r .= '<table cellspacing="0" class="catselect">';
1069      if( get_post_cat_setting($blog) == 3 )
1070      { // Main + Extra cats option is set, display header
1071          $r .= cat_select_header( $params );
1072      }
1073  
1074      if( get_allow_cross_posting() >= 2 ||
1075        ( isset( $blog) && get_post_cat_setting( $blog ) > 1 && get_allow_cross_posting() == 1 ) )
1076      { // If BLOG cross posting enabled, go through all blogs with cats:
1077          /**
1078           * @var BlogCache
1079           */
1080          $BlogCache = & get_BlogCache();
1081  
1082          /**
1083           * @var Blog
1084           */
1085          for( $l_Blog = & $BlogCache->get_first(); !is_null($l_Blog); $l_Blog = & $BlogCache->get_next() )
1086          { // run recursively through the cats
1087              if( ! blog_has_cats( $l_Blog->ID ) )
1088                  continue;
1089  
1090              if( ! $current_User->check_perm( 'blog_post_statuses', 'edit', false, $l_Blog->ID ) )
1091                  continue;
1092  
1093              $r .= '<tr class="group" id="catselect_blog'.$l_Blog->ID.'"><td colspan="3">'.$l_Blog->dget('name')."</td></tr>\n";
1094              $cat_sel_total_count++; // the header uses 1 line
1095  
1096              $current_blog_ID = $l_Blog->ID;    // Global needed in callbacks
1097              $r .= cat_children( $cache_categories, $l_Blog->ID, NULL, 'cat_select_before_first',
1098                                          'cat_select_before_each', 'cat_select_after_each', 'cat_select_after_last', 1 );
1099              if( $blog == $l_Blog->ID )
1100              {
1101                  $r .= cat_select_new();
1102              }
1103          }
1104      }
1105      else
1106      { // BLOG Cross posting is disabled. Current blog only:
1107          $current_blog_ID = $blog;
1108          $r .= cat_children( $cache_categories, $current_blog_ID, NULL, 'cat_select_before_first',
1109                                      'cat_select_before_each', 'cat_select_after_each', 'cat_select_after_last', 1 );
1110          $r .= cat_select_new();
1111      }
1112  
1113      $r .= '</table>';
1114  
1115      echo $r;
1116  
1117      if( isset($blog) && get_allow_cross_posting() )
1118      {
1119          echo '<script type="text/javascript">jQuery.getScript("'.$rsc_url.'js/jquery/jquery.scrollto.js", function () {
1120              jQuery("#itemform_categories").scrollTo( "#catselect_blog'.$blog.'" );
1121          });</script>';
1122      }
1123      $Form->end_fieldset();
1124  }
1125  
1126  /**
1127   * Header for {@link cat_select()}
1128   *
1129   * @param array Params
1130   */
1131  function cat_select_header( $params = array() )
1132  {
1133      $params = array_merge( array(
1134              'category_name'        => T_('Category'),
1135              'category_main_title'  => T_('Main category'),
1136              'category_extra_title' => T_('Additional category'),
1137          ), $params );
1138  
1139      // main cat header
1140      $r = '<thead><tr><th class="selector catsel_main" title="'.$params['category_main_title'].'">'.T_('Main').'</th>';
1141  
1142      // extra cat header
1143      $r .= '<th class="selector catsel_extra" title="'.$params['category_extra_title'].'">'.T_('Extra').'</th>';
1144  
1145      // category header
1146      $r .= '<th class="catsel_name">'.$params['category_name'].'</th><!--[if IE 7]><th width="1"><!-- for IE7 --></th><![endif]--></tr></thead>';
1147  
1148      return $r;
1149  }
1150  
1151  /**
1152   * callback to start sublist
1153   */
1154  function cat_select_before_first( $parent_cat_ID, $level )
1155  { // callback to start sublist
1156      return ''; // "\n<ul>\n";
1157  }
1158  
1159  /**
1160   * callback to display sublist element
1161   */
1162  function cat_select_before_each( $cat_ID, $level, $total_count )
1163  { // callback to display sublist element
1164      global $current_blog_ID, $blog, $post_extracats, $edited_Item, $current_User;
1165      global $creating, $cat_select_level, $cat_select_form_fields;
1166      global $cat_sel_total_count;
1167  
1168      $ChapterCache = & get_ChapterCache();
1169      $thisChapter = $ChapterCache->get_by_ID($cat_ID);
1170  
1171      if( $thisChapter->lock && !$current_User->check_perm( 'blog_cats', '', false, $current_blog_ID ) )
1172      {    // This chapter is locked and current user has no permission to edit the categories of this blog
1173          return;
1174      }
1175  
1176      $cat_sel_total_count++;
1177  
1178      $r = "\n".'<tr class="'.( $total_count%2 ? 'odd' : 'even' ).'">';
1179  
1180      // RADIO for main cat:
1181      if( get_post_cat_setting($blog) != 2 )
1182      { // if no "Multiple categories per post" option is set display radio
1183          if( !$thisChapter->meta
1184              && ( ($current_blog_ID == $blog) || (get_allow_cross_posting( $blog ) >= 2) ) )
1185          { // This is current blog or we allow moving posts accross blogs
1186              if( $cat_select_form_fields )
1187              {    // We want a form field:
1188                  $r .= '<td class="selector catsel_main"><input type="radio" name="post_category" class="checkbox" title="'
1189                              .T_('Select as MAIN category').'" value="'.$cat_ID.'"';
1190                  if( $cat_ID == $edited_Item->main_cat_ID )
1191                  { // main cat of the Item or set as default main cat above
1192                      $r .= ' checked="checked"';
1193                  }
1194                  $r .= ' id="sel_maincat_'.$cat_ID.'"';
1195                  $r .= ' onclick="check_extracat(this);" /></td>';
1196              }
1197              else
1198              {    // We just want info:
1199                  $r .= '<td class="selector catsel_main">'.bullet( $cat_ID == $edited_Item->main_cat_ID ).'</td>';
1200              }
1201          }
1202          else
1203          { // Don't allow to select this cat as a main cat
1204              $r .= '<td class="selector catsel_main">&nbsp;</td>';
1205          }
1206      }
1207  
1208      // CHECKBOX:
1209      if( get_post_cat_setting( $blog ) >= 2 )
1210      { // We allow multiple categories or main + extra cat,  display checkbox:
1211          if( !$thisChapter->meta
1212              && ( ($current_blog_ID == $blog) || ( get_allow_cross_posting( $blog ) % 2 == 1 )
1213                  || ( ( get_allow_cross_posting( $blog ) == 2 ) && ( get_post_cat_setting( $blog ) == 2 ) ) ) )
1214          { // This is the current blog or we allow cross posting (select extra cat from another blog)
1215              if( $cat_select_form_fields )
1216              {    // We want a form field:
1217                  $r .= '<td class="selector catsel_extra"><input type="checkbox" name="post_extracats[]" class="checkbox" title="'
1218                              .T_('Select as an additional category').'" value="'.$cat_ID.'"';
1219                  // if( ($cat_ID == $edited_Item->main_cat_ID) || (in_array( $cat_ID, $post_extracats )) )  <--- We don't want to precheck the default cat because it will stay checked if we change the default main. On edit, the checkbox will always be in the array.
1220                  if( in_array( $cat_ID, $post_extracats ) )
1221                  { // This category was selected
1222                      $r .= ' checked="checked"';
1223                  }
1224                  $r .= ' id="sel_extracat_'.$cat_ID.'"';
1225                  $r .= ' /></td>';
1226              }
1227              else
1228              {    // We just want info:
1229                  $r .= '<td class="selector catsel_main">'.bullet( ($cat_ID == $edited_Item->main_cat_ID) || (in_array( $cat_ID, $post_extracats )) ).'</td>';
1230              }
1231          }
1232          else
1233          { // Don't allow to select this cat as an extra cat
1234              $r .= '<td class="selector catsel_main">&nbsp;</td>';
1235          }
1236      }
1237  
1238      $additional_style = '';
1239  
1240      if( $thisChapter->meta )
1241      {    // Change style of meta category
1242          $additional_style .= 'font-weight:bold;';
1243      }
1244  
1245      $chapter_lock_status = '';
1246      if( $thisChapter->lock )
1247      {    // Add icon for locked category
1248          $chapter_lock_status = '<span style="padding-left:5px">'.get_icon( 'file_not_allowed', 'imgtag', array( 'title' => T_('Locked')) ).'</span>';
1249      }
1250  
1251      $BlogCache = & get_BlogCache();
1252      $r .= '<td class="catsel_name"><label'
1253                  .' for="'.( get_post_cat_setting( $blog ) == 2
1254                      ? 'sel_extracat_'.$cat_ID
1255                      : 'sel_maincat_'.$cat_ID ).'"'
1256                  .' style="padding-left:'.($level-1).'em;'.$additional_style.'">'
1257                  .$thisChapter->name.'</label>'
1258                  .$chapter_lock_status
1259                  .' <a href="'.htmlspecialchars($thisChapter->get_permanent_url()).'" title="'.htmlspecialchars(T_('View category in blog.')).'">'
1260                  .'&nbsp;&raquo;&nbsp;' // TODO: dh> provide an icon instead? // fp> maybe the A(dmin)/B(log) icon from the toolbar? And also use it for permalinks to posts?
1261                  .'</a></td>'
1262                  .'<!--[if IE 7]><td width="1"><!-- for IE7 --></td><![endif]--></tr>'
1263                  ."\n";
1264  
1265      return $r;
1266  }
1267  
1268  /**
1269   * callback after each sublist element
1270   */
1271  function cat_select_after_each( $cat_ID, $level )
1272  { // callback after each sublist element
1273      return '';
1274  }
1275  
1276  /**
1277   * callback to end sublist
1278   */
1279  function cat_select_after_last( $parent_cat_ID, $level )
1280  { // callback to end sublist
1281      return ''; // "</ul>\n";
1282  }
1283  
1284  /**
1285   * new category line for catselect table
1286   * @return string new row code
1287   */
1288  function cat_select_new()
1289  {
1290      global $blog, $current_User;
1291  
1292      if( ! $current_User->check_perm( 'blog_cats', '', false, $blog ) )
1293      {    // Current user cannot add/edit a categories for this blog
1294          return '';
1295      }
1296  
1297      $new_maincat = param( 'new_maincat', 'boolean', false );
1298      $new_extracat = param( 'new_extracat', 'boolean', false );
1299      if( $new_maincat || $new_extracat )
1300      {
1301          $category_name = param( 'category_name', 'string', '' );
1302      }
1303      else
1304      {
1305          $category_name = '';
1306      }
1307  
1308      global $cat_sel_total_count;
1309      $cat_sel_total_count++;
1310      $r = "\n".'<tr class="'.( $cat_sel_total_count%2 ? 'odd' : 'even' ).'">';
1311  
1312      if( get_post_cat_setting( $blog ) != 2 )
1313      {
1314          // RADIO for new main cat:
1315          $r .= '<td class="selector catsel_main"><input type="radio" name="post_category" class="checkbox" title="'
1316                              .T_('Select as MAIN category').'" value="0"';
1317          if( $new_maincat )
1318          {
1319              $r.= ' checked="checked"';
1320          }
1321          $r .= ' id="sel_maincat_new"';
1322          $r .= ' onclick="check_extracat(this);"';
1323          $r .= '/></td>';
1324      }
1325  
1326      if( get_post_cat_setting( $blog ) >= 2 )
1327      {
1328          // CHECKBOX
1329          $r .= '<td class="selector catsel_extra"><input type="checkbox" name="post_extracats[]" class="checkbox" title="'
1330                              .T_('Select as an additional category').'" value="0"';
1331          if( $new_extracat )
1332          {
1333              $r.= ' checked="checked"';
1334          }
1335          $r .= 'id="sel_extracat_new"/></td>';
1336      }
1337  
1338      // INPUT TEXT for new category name
1339      $r .= '<td class="catsel_name">'
1340                  .'<input maxlength="255" style="width: 100%;" value="'.$category_name.'" size="20" type="text" name="category_name" id="new_category_name" />'
1341                  ."</td>";
1342      $r .= '<!--[if IE 7]><td width="1">&nbsp</td><![endif]-->';
1343      $r .= "</tr>";
1344      return $r;
1345  }
1346  
1347  
1348  /**
1349   * Used by the items & the comments controllers
1350   *
1351   * @param boolean TRUE - to display third level tabs, FALSE - to hide them(used in the edit mode)
1352   */
1353  function attach_browse_tabs( $display_tabs3 = true )
1354  {
1355      global $AdminUI, $Blog, $current_User, $dispatcher, $ItemTypeCache;
1356  
1357      $menu_entries = array(
1358              'full' => array(
1359                  'text' => T_('All'),
1360                  'href' => $dispatcher.'?ctrl=items&amp;tab=full&amp;filter=restore&amp;blog='.$Blog->ID,
1361                  ),
1362          );
1363  
1364      if( $Blog->get( 'type' ) == 'manual' )
1365      { // Display this tab only for manual blogs
1366          $menu_entries = array_merge( $menu_entries, array(
1367                  'manual' => array(
1368                      'text' => T_('Manual Pages'),
1369                      'href' => $dispatcher.'?ctrl=items&amp;tab=manual&amp;filter=restore&amp;blog='.$Blog->ID,
1370                      ),
1371              ) );
1372      }
1373  
1374      $menu_entries = array_merge( $menu_entries, array(
1375              'list' => array(
1376                  'text' => T_('Posts'),
1377                  'href' => $dispatcher.'?ctrl=items&amp;tab=list&amp;filter=restore&amp;blog='.$Blog->ID,
1378                  ),
1379              'pages' => array(
1380                  'text' => T_('Pages'),
1381                  'href' => $dispatcher.'?ctrl=items&amp;tab=pages&amp;filter=restore&amp;blog='.$Blog->ID,
1382                  ),
1383              'intros' => array(
1384                  'text' => T_('Intros'),
1385                  'href' => $dispatcher.'?ctrl=items&amp;tab=intros&amp;filter=restore&amp;blog='.$Blog->ID,
1386                  ),
1387              'podcasts' => array(
1388                  'text' => T_('Podcasts'),
1389                  'href' => $dispatcher.'?ctrl=items&amp;tab=podcasts&amp;filter=restore&amp;blog='.$Blog->ID,
1390                  ),
1391              'links' => array(
1392                  'text' => T_('Sidebar links'),
1393                  'href' => $dispatcher.'?ctrl=items&amp;tab=links&amp;filter=restore&amp;blog='.$Blog->ID,
1394                  ),
1395              'ads' => array(
1396                  'text' => T_('Advertisements'),
1397                  'href' => $dispatcher.'?ctrl=items&amp;tab=ads&amp;filter=restore&amp;blog='.$Blog->ID,
1398                  ),
1399          ) );
1400  
1401      $AdminUI->add_menu_entries( 'items', $menu_entries );
1402  
1403      /* fp> Custom types should be variations of normal posts by default
1404        I am ok with giving extra tabs to SOME of them but then the
1405          posttype list has to be transformed into a normal CREATE/UPDATE/DELETE (CRUD)
1406          (see the stats>goals CRUD for an example of a clean CRUD)
1407          and each post type must have a checkbox like "use separate tab"
1408          Note: you can also make a select list "group with: posts|pages|etc...|other|own tab"
1409  
1410          $ItemTypeCache = & get_ItemTypeCache();
1411          $default_post_types = array(1,1000,1500,1520,1530,1570,1600,2000,3000,4000,5000);
1412          $items_types = array_values( array_keys( $ItemTypeCache->get_option_array() ) );
1413          // a tab for custom types
1414          if ( array_diff($items_types,$default_post_types) )
1415          {
1416              $AdminUI->add_menu_entries(
1417                      'items',
1418                      array(
1419                          'custom' => array(
1420                              'text' => T_('Custom Types'),
1421                              'href' => $dispatcher.'?ctrl=items&amp;tab=custom&amp;filter=restore&amp;blog='.$Blog->ID,
1422                          ),
1423                      )
1424              );
1425          }
1426      */
1427  
1428      if( $Blog->get_setting( 'use_workflow' ) )
1429      {    // We want to use workflow properties for this blog:
1430          $AdminUI->add_menu_entries(
1431                  'items',
1432                  array(
1433                          'tracker' => array(
1434                              'text' => T_('Tracker'),
1435                              'href' => $dispatcher.'?ctrl=items&amp;tab=tracker&amp;filter=restore&amp;blog='.$Blog->ID,
1436                              ),
1437                      )
1438              );
1439      }
1440  
1441      if( $current_User->check_perm( 'blog_comments', 'edit', false, $Blog->ID ) )
1442      { // User has permission to edit published, draft or deprecated comments (at least one kind)
1443          $AdminUI->add_menu_entries(
1444                  'items',
1445                  array(
1446                          'comments' => array(
1447                              'text' => T_('Comments'),
1448                              'href' => $dispatcher.'?ctrl=comments&amp;blog='.$Blog->ID.'&amp;filter=restore',
1449                              ),
1450                      )
1451              );
1452  
1453          if( $display_tabs3 )
1454          {
1455              $AdminUI->add_menu_entries( array('items','comments'), array(
1456                      'fullview' => array(
1457                          'text' => T_('Full text view'),
1458                          'href' => $dispatcher.'?ctrl=comments&amp;tab3=fullview&amp;filter=restore&amp;blog='.$Blog->ID
1459                          ),
1460                      'listview' => array(
1461                          'text' => T_('List view'),
1462                          'href' => $dispatcher.'?ctrl=comments&amp;tab3=listview&amp;filter=restore&amp;blog='.$Blog->ID
1463                          ),
1464                      )
1465                  );
1466          }
1467      }
1468  
1469  
1470      // What perms do we have?
1471      $coll_settings_perm = $current_User->check_perm( 'options', 'view', false, $Blog->ID );
1472      $settings_url = '?ctrl=itemtypes&amp;tab=settings&amp;tab3=types&amp;blog='.$Blog->ID;
1473      if( $coll_chapters_perm = $current_User->check_perm( 'blog_cats', '', false, $Blog->ID ) )
1474      {
1475          $settings_url = '?ctrl=chapters&amp;blog='.$Blog->ID;
1476      }
1477  
1478      if( $coll_settings_perm || $coll_chapters_perm )
1479      {
1480          $AdminUI->add_menu_entries(
1481              'items',
1482              array(
1483                  'settings' => array(
1484                      'text' => T_('Content settings'),
1485                      'href' => $settings_url,
1486                      )
1487                  )
1488              );
1489  
1490          if( $coll_chapters_perm )
1491          {
1492              $AdminUI->add_menu_entries( array('items','settings'), array(
1493                  'chapters' => array(
1494                      'text' => T_('Categories'),
1495                      'href' => '?ctrl=chapters&amp;blog='.$Blog->ID
1496                      ),
1497                  )
1498              );
1499          }
1500  
1501          if( $coll_settings_perm )
1502          {
1503              $AdminUI->add_menu_entries( array('items','settings'), array(
1504                  'types' => array(
1505                      'text' => T_('Post types'),
1506                      'title' => T_('Post types management'),
1507                      'href' => '?ctrl=itemtypes&amp;tab=settings&amp;tab3=types&amp;blog='.$Blog->ID
1508                      ),
1509                  'statuses' => array(
1510                      'text' => T_('Post statuses'),
1511                      'title' => T_('Post statuses management'),
1512                      'href' => '?ctrl=itemstatuses&amp;tab=settings&amp;tab3=statuses&amp;blog='.$Blog->ID
1513                      ),
1514                  )
1515              );
1516          }
1517      }
1518  }
1519  
1520  
1521  
1522  /**
1523   * Allow to select status/visibility
1524   *
1525   * @param object Form
1526   * @param string Status
1527   * @param boolean Mass create
1528   * @param array labels of statuses
1529   */
1530  function visibility_select( & $Form, $post_status, $mass_create = false, $labels = array(), $field_label = '' )
1531  {
1532      $labels = array_merge( get_visibility_statuses('notes-array'), $labels );
1533  
1534      global $current_User, $Blog;
1535  
1536      $mass_create_statuses = array( 'redirected' );
1537  
1538      $sharing_options = array();
1539  
1540      foreach( $labels as $status => $label )
1541      {
1542          if( $current_User->check_perm( 'blog_post!'.$status, 'create', false, $Blog->ID ) &&
1543              ( !in_array( $status, $mass_create_statuses ) || !$mass_create ) )
1544          {
1545              $sharing_options[] = array( $status, $label[0].' <span class="notes">'.$label[1].'</span>' );
1546          }
1547      }
1548  
1549      if( count( $sharing_options ) == 1 )
1550      { // Only one status is available, don't show visibility select
1551          $Form->hidden( 'post_status', $sharing_options[0][0] );
1552          return;
1553      }
1554  
1555      $Form->radio( 'post_status', $post_status, $sharing_options, $field_label, true );
1556  }
1557  
1558  
1559  /**
1560   * Selection of the issue date
1561   *
1562   * @todo dh> should display erroneous values (e.g. when giving invalid date) as current (form) value, too.
1563   * @param Form
1564   * @param boolean Break line
1565   * @param string Title
1566   */
1567  function issue_date_control( $Form, $break = false, $field_title = '' )
1568  {
1569      global $edited_Item;
1570  
1571      if( $field_title == '' )
1572      {
1573          $field_title = T_('Issue date');
1574      }
1575  
1576      echo $field_title.':<br />';
1577  
1578      echo '<label><input type="radio" name="item_dateset" id="set_issue_date_now" value="0" '
1579                  .( ($edited_Item->dateset == 0) ? 'checked="checked"' : '' )
1580                  .'/><strong>'.T_('Update to NOW').'</strong></label>';
1581  
1582      if( $break )
1583      {
1584          echo '<br />';
1585      }
1586  
1587      echo '<label><input type="radio" name="item_dateset" id="set_issue_date_to" value="1" '
1588                  .( ($edited_Item->dateset == 1) ? 'checked="checked"' : '' )
1589                  .'/><strong>'.T_('Set to').':</strong></label>';
1590      $Form->date( 'item_issue_date', $edited_Item->get('issue_date'), '' );
1591      echo ' '; // allow wrapping!
1592      $Form->time( 'item_issue_time', $edited_Item->get('issue_date'), '', 'hh:mm:ss', '' );
1593      echo ' '; // allow wrapping!
1594  
1595      // Autoselect "change date" is the date is changed.
1596      ?>
1597      <script>
1598      jQuery( function()
1599              {
1600                  jQuery('#item_issue_date, #item_issue_time').change(function()
1601                  {
1602                      jQuery('#set_issue_date_to').attr("checked", "checked")
1603                  })
1604              }
1605          )
1606      </script>
1607      <?php
1608  
1609  }
1610  
1611  
1612  /**
1613   * Template tag: Link to an item identified by its url title / slug / name
1614   *
1615   * Note: this will query the database. Thus, in most situations it will make more sense
1616   * to use a hardcoded link. This tag can be useful for prototyping location independant
1617   * sites.
1618   */
1619  function item_link_by_urltitle( $params = array() )
1620  {
1621      // Make sure we are not missing any param:
1622      $params = array_merge( array(
1623              'urltitle'    => NULL,  // MUST BE SPECIFIED
1624              'before'      => ' ',
1625              'after'       => ' ',
1626              'text'        => '#',
1627          ), $params );
1628  
1629    /**
1630       * @var ItemCache
1631       */
1632      $ItemCache = & get_ItemCache();
1633  
1634    /**
1635       * @var Item
1636       */
1637      $Item = & $ItemCache->get_by_urltitle( $params['urltitle'], false );
1638  
1639      if( empty($Item) )
1640      {
1641          return false;
1642      }
1643  
1644      $Item->permanent_link( $params );
1645  }
1646  
1647  
1648  /**
1649   * Load new status when the item was published
1650   * If the status is invalid then an error message will be added to messages
1651   *
1652   * @param boolean true if creating new post, false on update
1653   * @return string the publish status or an allowed status if the given status was invalid
1654   */
1655  function load_publish_status( $creating = false )
1656  {
1657      global $Messages;
1658  
1659      $publish_status = param( 'publish_status', 'string', '' );
1660      if( ! in_array( $publish_status, array( 'published', 'community', 'protected', 'private' ) ) )
1661      { // Publish with the given status doesn't exists "published"
1662          if( $creating )
1663          {
1664              $Messages->add( T_( 'The post has been created but not published because it seems like you wanted to publish with an invalid status.'), 'error' );
1665          }
1666          else
1667          {
1668              $Messages->add( T_( 'The post has been updated but not published because it seems like you wanted to publish with an invalid status.'), 'error' );
1669          }
1670          $publish_status = 'draft';
1671      }
1672      return $publish_status;
1673  }
1674  
1675  
1676  function echo_publish_buttons( $Form, $creating, $edited_Item, $inskin = false, $display_preview = false )
1677  {
1678      global $Blog, $current_User;
1679      global $next_action, $highest_publish_status; // needs to be passed out for echo_publishnowbutton_js( $action )
1680  
1681      // ---------- PREVIEW ----------
1682      if( !$inskin || $display_preview )
1683      {
1684          $url = url_same_protocol( $Blog->get( 'url' ) ); // was dynurl
1685          $Form->button( array( 'button', '', T_('Preview'), 'PreviewButton', 'b2edit_open_preview(this.form, \''.$url.'\');' ) );
1686      }
1687  
1688      // ---------- SAVE ----------
1689      $next_action = ($creating ? 'create' : 'update');
1690      if( !$inskin )
1691      { // Show Save & Edit only on admin mode
1692          $Form->submit( array( 'actionArray['.$next_action.'_edit]', /* TRANS: This is the value of an input submit button */ T_('Save & edit'), 'SaveEditButton' ) );
1693      }
1694      $Form->submit( array( 'actionArray['.$next_action.']', /* TRANS: This is the value of an input submit button */ T_('Save'), 'SaveButton' ) );
1695  
1696      list( $highest_publish_status, $publish_text ) = get_highest_publish_status( 'post', $Blog->ID );
1697      if( !isset( $edited_Item->status ) )
1698      {
1699          $edited_Item->status = $highest_publish_status;
1700      }
1701      if( $edited_Item->status != $highest_publish_status )
1702      {    // Only allow publishing if in draft mode. Other modes are too special to run the risk of 1 click publication.
1703          $publish_style = 'display: inline';
1704      }
1705      else
1706      {
1707          $publish_style = 'display: none';
1708      }
1709      $Form->hidden( 'publish_status', $highest_publish_status );
1710      $Form->submit( array(
1711          'actionArray['.$next_action.'_publish]',
1712          /* TRANS: This is the value of an input submit button */ $publish_text,
1713          'SaveButton',
1714          '',
1715          $publish_style
1716      ) );
1717  }
1718  
1719  /**
1720   * Output JavaScript code to dynamically show popup files attachment window
1721   *
1722   * This is a part of the process that makes it smoother to "Save & start attaching files".
1723   */
1724  function echo_attaching_files_button_js( & $iframe_name )
1725  {
1726      global $dispatcher;
1727      global $edited_Item;
1728      $iframe_name = 'attach_'.generate_random_key( 16 );
1729      ?>
1730      <script type="text/javascript">
1731              pop_up_window( '<?php echo $dispatcher; ?>?ctrl=files&mode=upload&iframe_name=<?php echo $iframe_name ?>&fm_mode=link_object&link_type=item&link_object_ID=<?php echo $edited_Item->ID?>', 'fileman_upload', 1000 );
1732      </script>
1733      <?php
1734  }
1735  
1736  /**
1737   * Output JavaScript code to set hidden field is_attachments
1738   * which indicates that we must show attachments files popup window
1739   *
1740   * This is a part of the process that makes it smoother to "Save & start attaching files".
1741   */
1742  function echo_set_is_attachments()
1743  {
1744      ?>
1745      <script type="text/javascript">
1746          jQuery( '#itemform_createlinks td input' ).click( function()
1747          {
1748              jQuery( 'input[name=is_attachments]' ).attr("value", "true");
1749          } );
1750      </script>
1751      <?php
1752  }
1753  
1754  /**
1755   * Output JavaScript code for "Add/Link files" link
1756   *
1757   * This is a part of the process that makes it smoother to "Save & start attaching files".
1758   */
1759  function echo_link_files_js()
1760  {
1761      ?>
1762      <script type="text/javascript">
1763              jQuery( '#title_file_add' ).click( function()
1764              {
1765                  jQuery( 'input[name=is_attachments]' ).attr("value", "true");
1766                  jQuery( '#itemform_createlinks input[name="actionArray[create_edit]"]' ).click();
1767              } );
1768      </script>
1769      <?php
1770  }
1771  
1772  /**
1773   * Output JavaScript code to dynamically show or hide the "Publish NOW!"
1774   * button depending on the selected post status.
1775   *
1776   * This function is used by the simple and expert write screens.
1777   */
1778  function echo_publishnowbutton_js()
1779  {
1780      global $next_action, $highest_publish_status;
1781      ?>
1782      <script type="text/javascript">
1783          jQuery( '#itemform_visibility input[type=radio]' ).click( function()
1784          {
1785              var publishnow_btn = jQuery( '.edit_actions input[name="actionArray[<?php echo $next_action; ?>_publish]"]' );
1786              var public_status = '<?php echo $highest_publish_status; ?>';
1787  
1788              if( this.value == public_status || public_status == 'draft' )
1789              {    // Hide the "Publish NOW !" button:
1790                  publishnow_btn.css( 'display', 'none' );
1791              }
1792              else
1793              {    // Show the button:
1794                  publishnow_btn.css( 'display', 'inline' );
1795              }
1796          } );
1797      </script>
1798      <?php
1799  }
1800  
1801  
1802  /**
1803   * Output Javascript for tags autocompletion.
1804   * @todo dh> a more facebook like widget would be: http://plugins.jquery.com/project/facelist
1805   *           "ListBuilder" is being planned for jQuery UI: http://wiki.jqueryui.com/ListBuilder
1806   *
1807   * @param array Tags
1808   */
1809  function echo_autocomplete_tags( $tags = array() )
1810  {
1811      global $htsrv_url;
1812  
1813      // Initialize an array to pre-fill the tags input
1814      $prefilled_tags = array();
1815      if( !empty( $tags ) )
1816      {
1817          foreach( $tags as $tag_name )
1818          {
1819              $prefilled_tags[] = array( 'id' => $tag_name, 'title' => $tag_name );
1820          }
1821      }
1822  
1823      //echo <<<EOD
1824  ?>
1825      <script type="text/javascript">
1826      (function($){
1827          jQuery(function() {
1828              jQuery( '#item_tags' ).tokenInput(
1829                  '<?php echo $htsrv_url.'async.php?action=get_tags' ?>',
1830                  {
1831                      theme: 'facebook',
1832                      queryParam: 'term',
1833                      propertyToSearch: 'title',
1834                      tokenValue: 'title',
1835                      preventDuplicates: true,
1836                      prePopulate: <?php echo evo_json_encode( $prefilled_tags ) ?>,
1837                      hintText: '<?php echo TS_('Type in a tag') ?>',
1838                      noResultsText: '<?php echo TS_('No results') ?>',
1839                      searchingText: '<?php echo TS_('Searching...') ?>'
1840                  }
1841              );
1842          });
1843      })(jQuery);
1844      </script>
1845  <?php
1846  //EOD;
1847  }
1848  
1849  
1850  /**
1851   * Assert that the supplied post type can be used by the current user in
1852   * the post's extra categories' context.
1853   *
1854   * @param array The extra cats of the post.
1855   */
1856  function check_perm_posttype( $post_extracats )
1857  {
1858      global $posttypes_perms, $item_typ_ID, $current_User;
1859  
1860      static $posttype2perm = NULL;
1861      if( $posttype2perm === NULL )
1862      {    // "Reverse" the $posttypes_perms array:
1863          // Tblue> Possibly bloat; this function usually is invoked only
1864          //        once, thus it *may* be better to simply iterate through
1865          //        the $posttypes_perms array every time and look for the
1866          //        post type ID.
1867          foreach( $posttypes_perms as $l_permname => $l_posttypes )
1868          {
1869              foreach( $l_posttypes as $ll_posttype )
1870              {
1871                  $posttype2perm[$ll_posttype] = $l_permname;
1872              }
1873          }
1874      }
1875  
1876      // Tblue> Usually, when this function is invoked, item_typ_ID is not
1877      //        loaded yet... If it is, it doesn't get loaded again anyway.
1878      //        Item::load_from_Request() uses param() again, in case this
1879      //        function wasn't called yet when load_from_Request() gets
1880      //        called (does this happen?).
1881      param( 'item_typ_ID', 'integer', true /* require input */ );
1882      if( ! isset( $posttype2perm[$item_typ_ID] ) )
1883      {    // Allow usage:
1884          return;
1885      }
1886  
1887      // Check permission:
1888      $current_User->check_perm( 'cats_'.$posttype2perm[$item_typ_ID], 'edit', true /* assert */, $post_extracats );
1889  }
1890  
1891  
1892  /**
1893   * Mass create.
1894   *
1895   * Create multiple posts from one post.
1896   *
1897   * @param object Instance of Item class (by reference).
1898   * @param boolean true if create paragraphs at each line break
1899   * @return array The posts, by reference.
1900   */
1901  function & create_multiple_posts( & $Item, $linebreak = false )
1902  {
1903      $Items = array();
1904  
1905      // Parse text into titles and contents:
1906      $current_title = '';
1907      $current_data  = '';
1908  
1909      // Append a newline to the end of the original contents to make sure
1910      // that the last item gets created - this saves a second loop.
1911      foreach( explode( "\n", $Item->content."\n" ) as $line )
1912      {
1913          $line = trim( strip_tags( $line ) );
1914  
1915          if( $current_title === '' && $line !== '' )
1916          {    // We got a new title:
1917              $current_title = $line;
1918          }
1919          elseif( $current_title !== '' )
1920          {
1921              if( $line !== '' )
1922              {    // We got a new paragraph for this post:
1923                  if( $linebreak )
1924                  {
1925                      $current_data .= '<p>'.$line.'</p>';
1926                  }
1927                  else
1928                  {
1929                      $current_data .= $line.' ';
1930                  }
1931              }
1932              else
1933              {    // End of this post:
1934                  $new_Item = duplicate( $Item );
1935  
1936                  $new_Item->set_param( 'title', 'string', $current_title );
1937  
1938                  if( !$linebreak )
1939                  {
1940                      $current_data = trim( $current_data );
1941                  }
1942                  $new_Item->set_param( 'content', 'string', $current_data );
1943  
1944                  $Items[] = $new_Item;
1945  
1946                  $current_title = '';
1947                  $current_data  = '';
1948              }
1949          }
1950      }
1951  
1952      return $Items;
1953  }
1954  
1955  /**
1956   *
1957   * Check if new category needs to be created or not (after post editing).
1958   * If the new category radio is checked creates the new category and set it to post category
1959   * If the new category checkbox is checked creates the new category and set it to post extracat
1960   *
1961   * Function is called during post creation or post update
1962   *
1963   * @param Object Post category (by reference).
1964   * @param Array Post extra categories (by reference).
1965   * @return boolean true - if there is no new category, or new category created succesfull; false if new category creation failed.
1966   */
1967  function check_categories( & $post_category, & $post_extracats )
1968  {
1969      $post_category = param( 'post_category', 'integer', -1 );
1970      $post_extracats = param( 'post_extracats', 'array/integer', array() );
1971      global $Messages, $Blog, $blog;
1972  
1973      load_class( 'chapters/model/_chaptercache.class.php', 'ChapterCache' );
1974      $GenericCategoryCache = & get_ChapterCache();
1975  
1976      if( $post_category == -1 )
1977      { // no main cat select
1978          if( count( $post_extracats ) == 0 )
1979          { // no extra cat select
1980              $post_category = $Blog->get_default_cat_ID();
1981          }
1982          else
1983          { // first extracat become main_cat
1984              if( get_allow_cross_posting() >= 2 )
1985              { // allow moving posts between different blogs is enabled, set first selected cat as main cat
1986                  $post_category = $post_extracats[0];
1987              }
1988              else
1989              { // allow moving posts between different blogs is disabled - we need a main cat from $blog
1990                  foreach( $post_extracats as $cat )
1991                  {
1992                      if( get_catblog( $cat ) != $blog )
1993                      { // this cat is not from $blog
1994                          continue;
1995                      }
1996                      // set first cat from $blog as main cat
1997                      $post_category = $cat;
1998                      break;
1999                  }
2000                  if( $post_category == -1 )
2001                  { // wasn't cat selected from $blog select a default as main cat
2002                      $post_category = $Blog->get_default_cat_ID();
2003                  }
2004              }
2005          }
2006          if( $post_category )
2007          { // If main cat is not a new category, and has been autoselected
2008              $GenericCategory = & $GenericCategoryCache->get_by_ID( $post_category );
2009              $post_category_Blog = $GenericCategory->get_Blog();
2010              $Messages->add( sprintf( T_('The main category for this post has been automatically set to "%s" (Blog "%s")'),
2011                  $GenericCategory->get_name(), $post_category_Blog->get( 'name') ), 'warning' );
2012          }
2013      }
2014  
2015      if( ! $post_category || in_array( 0, $post_extracats ) )    // if category key is 0 => means it is a new category
2016      {
2017          global $current_User;
2018          if( ! $current_User->check_perm( 'blog_cats', '', false, $Blog->ID ) )
2019          {    // Current user cannot add a categories for this blog
2020              check_categories_nosave( $post_category, $post_extracats); // set up the category parameters
2021              $Messages->add( T_('You are not allowed to create a new category.'), 'error' );
2022              return false;
2023          }
2024  
2025          $category_name = param( 'category_name', 'string', true );
2026          if( $category_name == '' )
2027          {
2028              $show_error = ! $post_category;    // new main category without name => error message
2029              check_categories_nosave( $post_category, $post_extracats); // set up the category parameters
2030              if( $show_error )
2031              { // new main category without name
2032                  $Messages->add( T_('Please provide a name for new category.'), 'error' );
2033                  return false;
2034              }
2035              return true;
2036          }
2037  
2038          $new_GenericCategory = & $GenericCategoryCache->new_obj( NULL, $blog );    // create new category object
2039          $new_GenericCategory->set( 'name', $category_name );
2040          if( $new_GenericCategory->dbinsert() !== false )
2041          {
2042              $Messages->add( T_('New category created.'), 'success' );
2043              if( ! $post_category ) // if new category is main category
2044              {
2045                  $post_category = $new_GenericCategory->ID;    // set the new ID
2046              }
2047  
2048              if( ( $extracat_key = array_search( '0', $post_extracats ) ) || $post_extracats[0] == '0' )
2049              {
2050                  if( $extracat_key )
2051                  {
2052                      unset($post_extracats[$extracat_key]);
2053                  }
2054                  else
2055                  {
2056                      unset($post_extracats[0]);
2057                  }
2058                  $post_extracats[] = $new_GenericCategory->ID;
2059              }
2060  
2061              $GenericCategoryCache->add($new_GenericCategory);
2062          }
2063          else
2064          {
2065              $Messages->add( T_('New category creation failed.'), 'error' );
2066              return false;
2067          }
2068      }
2069  
2070      if( get_allow_cross_posting() == 2 )
2071      { // Extra cats in different blogs is disabled, check selected extra cats
2072          $post_category_blog = get_catblog( $post_category );
2073          $ignored_cats = '';
2074          foreach( $post_extracats as $key => $cat )
2075          {
2076              if( get_catblog( $cat ) != $post_category_blog )
2077              { // this cat is not from main category blog, it has to be ingnored
2078                  $GenericCategory = & $GenericCategoryCache->get_by_ID( $cat );
2079                  $ignored_cats = $ignored_cats.$GenericCategory->get_name().', ';
2080                  unset( $post_extracats[$key] );
2081              }
2082          }
2083          $ingnored_length = strlen( $ignored_cats );
2084          if( $ingnored_length > 2 )
2085          { // ingnore list is not empty
2086              global $current_User, $admin_url;
2087              if( $current_User->check_perm( 'options', 'view', false ) )
2088              {
2089                  $cross_posting_text = '<a href="'.$admin_url.'?ctrl=features">'.T_('cross-posting is disabled').'</a>';
2090              }
2091              else
2092              {
2093                  $cross_posting_text = T_('cross-posting is disabled');
2094              }
2095              $ignored_cats = substr( $ignored_cats, 0, $ingnored_length - 2 );
2096              $Messages->add( sprintf( T_('The category selection "%s" was ignored since %s'),
2097                  $ignored_cats, $cross_posting_text ), 'warning' );
2098          }
2099      }
2100  
2101      // make sure main cat is in extracat list and there are no duplicates
2102      $post_extracats[] = $post_category;
2103      $post_extracats = array_unique( $post_extracats );
2104  
2105      return true;
2106  }
2107  
2108  /*
2109   * Set up params for new category creation
2110   * Set main category to default category, if the current category does not exist yet
2111   * Delete non existing category from extracats
2112   * It is called after simple/expert tab switch, and can be called during post creation or modification
2113   *
2114   * @param Object Post category (by reference).
2115   * @param Array Post extra categories (by reference).
2116   *
2117   */
2118  function check_categories_nosave( & $post_category, & $post_extracats )
2119  {
2120      global $Blog;
2121      $post_category = param( 'post_category', 'integer', $Blog->get_default_cat_ID() );
2122      $post_extracats = param( 'post_extracats', 'array/integer', array() );
2123  
2124      if( ! $post_category )    // if category key is 0 => means it is a new category
2125      {
2126          $post_category = $Blog->get_default_cat_ID();
2127          param( 'new_maincat', 'boolean', 1 );
2128      }
2129  
2130      if( ! empty( $post_extracats) && ( ( $extracat_key = array_search( '0', $post_extracats ) ) || $post_extracats[0] == '0' ) )
2131      {
2132          param( 'new_extracat', 'boolean', 1 );
2133          if( $extracat_key )
2134          {
2135              unset($post_extracats[$extracat_key]);
2136          }
2137          else
2138          {
2139              unset($post_extracats[0]);
2140          }
2141      }
2142  }
2143  
2144  /*
2145   * check the new category radio button, if the new category text has changed
2146   */
2147  function echo_onchange_newcat()
2148  {
2149  ?>
2150      <script type="text/javascript">
2151          jQuery( '#new_category_name' ).keypress( function()
2152          {
2153              var newcategory_radio = jQuery( '#sel_maincat_new' );
2154              if( ! newcategory_radio.attr('checked') )
2155              {
2156                  newcategory_radio.attr('checked', true);
2157                  jQuery( '#sel_extracat_new' ).attr('checked', true);
2158              }
2159          } );
2160      </script>
2161  <?php
2162  }
2163  
2164  /**
2165   * Automatically update the slug field when a title is typed.
2166   *
2167   * Variable slug_changed hold true if slug was manually changed
2168   * (we already do not need autocomplete) and false in other case.
2169   */
2170  function echo_slug_filler()
2171  {
2172  ?>
2173      <script type="text/javascript">
2174          var slug_changed = false;
2175          jQuery( '#post_title' ).keyup( function()
2176          {
2177              if(!slug_changed)
2178              {
2179                  jQuery( '#post_urltitle' ).val( jQuery( '#post_title' ).val() );
2180              }
2181          } );
2182  
2183          jQuery( '#post_urltitle' ).change( function()
2184          {
2185              slug_changed = true;
2186              jQuery( '[name=slug_changed]' ).val( 1 );
2187          } );
2188      </script>
2189  <?php
2190  }
2191  
2192  
2193  /**
2194   * Set slug_changed to 1 for cases when it is not needed trim slug
2195   */
2196  function echo_set_slug_changed()
2197  {
2198  ?>
2199      <script type="text/javascript">
2200          jQuery( '[name=slug_changed]' ).val( 1 );
2201      </script>
2202  <?php
2203  }
2204  
2205  
2206  /**
2207   * Handle show_comments radioboxes on item list full view
2208   */
2209  function echo_show_comments_changed()
2210  {
2211  ?>
2212      <script type="text/javascript">
2213          jQuery( '[name ^= show_comments]' ).change( function()
2214          {
2215              var item_id = jQuery('#comments_container').attr('value');
2216              if( ! isDefined( item_id) )
2217              { // if item_id is not defined, we have to show all comments from current blog
2218                  item_id = -1;
2219              }
2220              refresh_item_comments( item_id, 1 );
2221          } );
2222      </script>
2223  <?php
2224  }
2225  
2226  
2227  /**
2228   * Make location fields are not required for special posts
2229   */
2230  function echo_onchange_item_type_js()
2231  {
2232      global $posttypes_specialtypes;
2233  
2234  ?>
2235      <script type="text/javascript">
2236          var item_special_types = [<?php echo implode( ',', $posttypes_specialtypes ) ?>];
2237          jQuery( '#item_typ_ID' ).change( function()
2238          {
2239              for( var i in item_special_types )
2240              {
2241                  if( item_special_types[i] == jQuery( this ).val() )
2242                  {
2243                      jQuery( '#item_locations' ).addClass( 'not_required' );
2244                      return true;
2245                  }
2246              }
2247              jQuery( '#item_locations' ).removeClass( 'not_required' );
2248          } );
2249      </script>
2250  <?php
2251  }
2252  
2253  
2254  /**
2255   * Display CommentList with the given filters
2256   *
2257   * @param integer Blog ID
2258   * @param integer Item ID
2259   * @param array Status filters
2260   * @param integer Limit
2261   * @param array Comments IDs string to exclude from the list
2262   * @param string Filterset name
2263   * @param string Expiry status: 'all', 'active', 'expired'
2264   */
2265  function echo_item_comments( $blog_ID, $item_ID, $statuses = NULL, $currentpage = 1, $limit = 20, $comment_IDs = array(), $filterset_name = '', $expiry_status = 'active' )
2266  {
2267      global $inc_path, $status_list, $Blog, $admin_url;
2268  
2269      $BlogCache = & get_BlogCache();
2270      $Blog = & $BlogCache->get_by_ID( $blog_ID, false, false );
2271  
2272      global $CommentList;
2273      $CommentList = new CommentList2( $Blog, $limit, 'CommentCache', '', $filterset_name );
2274  
2275      $exlude_ID_list = NULL;
2276      if( !empty($comment_IDs) )
2277      {
2278          $exlude_ID_list = '-'.implode( ",", $comment_IDs );
2279      }
2280  
2281      if( empty( $statuses ) )
2282      {
2283          $statuses = get_visibility_statuses( 'keys', array( 'redirected', 'trash' ) );
2284      }
2285  
2286      if( $expiry_status == 'all' )
2287      { // Display all comments
2288          $expiry_statuses = array( 'active', 'expired' );
2289      }
2290      else
2291      { // Display active or expired comments
2292          $expiry_statuses = array( $expiry_status );
2293      }
2294  
2295      // if item_ID == -1 then don't use item filter! display all comments from current blog
2296      if( $item_ID == -1 )
2297      {
2298          $item_ID = NULL;
2299      }
2300      // set redirect_to
2301      if( $item_ID != null )
2302      { // redirect to the items full view
2303          param( 'redirect_to', 'url', url_add_param( $admin_url, 'ctrl=items&blog='.$blog_ID.'&p='.$item_ID, '&' ) );
2304          param( 'item_id', 'integer', $item_ID );
2305          param( 'currentpage', 'integer', $currentpage );
2306          if( count( $statuses ) == 1 )
2307          {
2308              $show_comments = $statuses[0];
2309          }
2310          else
2311          {
2312              $show_comments = 'all';
2313          }
2314          param( 'comments_number', 'integer', generic_ctp_number( $item_ID, 'comments', $show_comments ) );
2315          // Filter list:
2316          $CommentList->set_filters( array(
2317              'types' => array( 'comment', 'trackback', 'pingback' ),
2318              'statuses' => $statuses,
2319              'expiry_statuses' => $expiry_statuses,
2320              'comment_ID_list' => $exlude_ID_list,
2321              'post_ID' => $item_ID,
2322              'order' => 'ASC',//$order,
2323              'comments' => $limit,
2324              'page' => $currentpage,
2325          ) );
2326      }
2327      else
2328      { // redirect to the comments full view
2329          param( 'redirect_to', 'url', url_add_param( $admin_url, 'ctrl=comments&blog='.$blog_ID.'&filter=restore', '&' ) );
2330          // this is an ajax call we always have to restore the filterst (we can set filters only without ajax call)
2331          $CommentList->set_filters( array(
2332              'types' => array( 'comment', 'trackback', 'pingback' ),
2333          ) );
2334          $CommentList->restore_filterset();
2335      }
2336  
2337      // Get ready for display (runs the query):
2338      $CommentList->display_init();
2339  
2340      $CommentList->display_if_empty( array(
2341          'before'    => '<div class="bComment"><p>',
2342          'after'     => '</p></div>',
2343          'msg_empty' => T_('No feedback for this post yet...'),
2344      ) );
2345  
2346      // display comments
2347      require $inc_path.'comments/views/_comment_list.inc.php';
2348  }
2349  
2350  
2351  /**
2352   * Display a comment corresponding the given comment id
2353   *
2354   * @param int comment id
2355   * @param string where to redirect after comment edit
2356   * @param boolean true to set the new redirect param, false otherwise
2357   */
2358  function echo_comment( $comment_ID, $redirect_to = NULL, $save_context = false )
2359  {
2360      global $current_User, $localtimenow;
2361  
2362      $CommentCache = & get_CommentCache();
2363      /**
2364      * @var Comment
2365      */
2366      $Comment = $CommentCache->get_by_ID( $comment_ID );
2367      $Item = & $Comment->get_Item();
2368      $Blog = & $Item->get_Blog();
2369  
2370      $is_published = ( $Comment->get( 'status' ) == 'published' );
2371      $expiry_delay = $Item->get_setting( 'post_expiry_delay' );
2372      $is_expired = ( !empty( $expiry_delay ) && ( ( $localtimenow - mysql2timestamp( $Comment->get( 'date' ) ) ) > $expiry_delay ) );
2373  
2374      echo '<div id="c'.$comment_ID.'" class="bComment bComment';
2375      // check if comment is expired
2376      if( $is_expired )
2377      { // comment is expired
2378          echo 'expired';
2379      }
2380      else
2381      { // comment is not expired
2382          $Comment->status('raw');
2383      }
2384      echo '">';
2385  
2386      if( $current_User->check_perm( 'comment!CURSTATUS', 'moderate', false, $Comment ) )
2387      {    // User can moderate this comment
2388          echo '<div class="bSmallHead">';
2389          echo '<div>';
2390  
2391          echo '<div class="bSmallHeadRight">';
2392          $Comment->permanent_link( array(
2393                  'before' => '',
2394                  'text'   => '#text#'
2395              ) );
2396          echo '</div>';
2397  
2398          echo '<span class="bDate">';
2399          $Comment->date();
2400          echo '</span>@<span class = "bTime">';
2401          $Comment->time( 'H:i' );
2402          echo '</span>';
2403  
2404          $Comment->author_email( '', ' &middot; Email: <span class="bEmail">', '</span>' );
2405          echo ' &middot; <span class="bKarma">';
2406          $Comment->spam_karma( T_('Spam Karma').': %s%', T_('No Spam Karma') );
2407          echo '</span>';
2408  
2409          echo '</div>';
2410          echo '<div style="padding-top:3px">';
2411          if( $is_expired )
2412          {
2413              echo '<div class="bSmallHeadRight">';
2414              echo '<span class="bExpired">'.T_('EXPIRED').'</span>';
2415              echo '</div>';
2416          }
2417          $Comment->author_ip( 'IP: <span class="bIP">', '</span> &middot; ', true );
2418          $Comment->ip_country( '', ' &middot; ' );
2419          $Comment->author_url_with_actions( /*$redirect_to*/'', true, true );
2420          echo '</div>';
2421          echo '</div>';
2422  
2423          echo '<div class="bCommentContent">';
2424          $Comment->status( 'styled' );
2425          echo '<div class="bTitle">';
2426          echo T_('In response to:')
2427                  .' <a href="?ctrl=items&amp;blog='.$Blog->ID.'&amp;p='.$Item->ID.'">'.$Item->dget('title').'</a>';
2428          echo '</div>';
2429          echo '<div class="bCommentTitle">';
2430          echo $Comment->get_title();
2431          echo '</div>';
2432          echo '<div class="bCommentText">';
2433          $Comment->rating();
2434          $Comment->avatar();
2435          $Comment->content( 'htmlbody', 'true' );
2436          echo '</div>';
2437          echo '</div>';
2438  
2439          echo '<div class="CommentActionsArea">';
2440  
2441          echo '<div class="floatleft">';
2442  
2443          // Display edit button if current user has the rights:
2444          $Comment->edit_link( ' ', ' ', get_icon( 'edit' ), '#', 'roundbutton', '&amp;', $save_context, $redirect_to );
2445  
2446          echo '<span class="roundbutton_group">';
2447          // Display publish NOW button if current user has the rights:
2448          $link_params = array(
2449              'class'        => 'roundbutton_text',
2450              'save_context' => $save_context,
2451              'ajax_button'  => true,
2452              'redirect_to'  => $redirect_to,
2453          );
2454          $Comment->raise_link( $link_params );
2455  
2456          // Display deprecate button if current user has the rights:
2457          $Comment->lower_link( $link_params );
2458  
2459          $next_status_in_row = $Comment->get_next_status( false );
2460          if( $next_status_in_row && $next_status_in_row[0] != 'deprecated' )
2461          { // Display deprecate button if current user has the rights:
2462              $Comment->deprecate_link( '', '', get_icon( 'move_down_grey', 'imgtag', array( 'title' => '' ) ), '#', 'roundbutton', '&amp;', true, true );
2463          }
2464  
2465          // Display delete button if current user has the rights:
2466          $Comment->delete_link( '', '', '#', '#', 'roundbutton_text', false, '&amp;', $save_context, true, '#', $redirect_to );
2467          echo '</span>';
2468  
2469          echo '</div>';
2470  
2471          // Display Spam Voting system
2472          $Comment->vote_spam( '', '', '&amp;', $save_context, true );
2473  
2474          echo '<div class="clear"></div>';
2475          echo '</div>';
2476      }
2477      else
2478      {    // No permissions to moderate of this comment, just preview
2479          echo '<div class="bSmallHead">';
2480          echo '<div>';
2481  
2482          echo '<div class="bSmallHeadRight">';
2483          echo T_('Visibility').': ';
2484          echo '<span class="bStatus">';
2485          $Comment->status();
2486          echo '</span>';
2487          echo '</div>';
2488  
2489          echo '<span class="bDate">';
2490          $Comment->date();
2491          echo '</span>@<span class = "bTime">';
2492          $Comment->time( 'H:i' );
2493          echo '</span>';
2494  
2495          echo '</div>';
2496          echo '</div>';
2497  
2498          if( $is_published )
2499          {
2500              echo '<div class="bCommentContent">';
2501              echo '<div class="bCommentTitle">';
2502              echo $Comment->get_title();
2503              echo '</div>';
2504              echo '<div class="bCommentText">';
2505              $Comment->rating();
2506              $Comment->avatar();
2507              $Comment->content();
2508              echo '</div>';
2509              echo '</div>';
2510          }
2511  
2512          echo '<div class="clear"></div>';
2513      }
2514  
2515      echo '</div>'; // end
2516  }
2517  
2518  
2519  /**
2520   * Display a page link on item full view
2521   *
2522   * @param integer the item id
2523   * @param string link text
2524   * @param integer the page number
2525   */
2526  function echo_pagenumber( $item_ID, $text, $value )
2527  {
2528      echo ' <a href="javascript:startRefreshComments( '.$item_ID.', '.$value.' )">'.$text.'</a>';
2529  }
2530  
2531  
2532  /**
2533   * Display page links on item full view
2534   *
2535   * @param integer the item id
2536   * @param integer current page number
2537   * @param integer all comments number in the list
2538   * @param array Params
2539   */
2540  function echo_pages( $item_ID, $currentpage, $comments_number, $params = array() )
2541  {
2542      $params = array_merge( array(
2543              'list_span'  => 11, // The number of pages to display for one time
2544              'page_size'  => 20, // The number of comments on one page
2545              'list_start' => '<div class="results_nav" id="paging">',
2546              'list_end'   => '</div>',
2547              'prev_text'  => T_('Previous'),
2548              'next_text'  => T_('Next'),
2549              'pages_text' => '<strong>'.T_('Pages').'</strong>:',
2550          ), $params );
2551  
2552      if( $comments_number == 0 )
2553      { // No comments
2554          return;
2555      }
2556  
2557      $total_pages = ceil( $comments_number / $params['page_size'] );
2558  
2559      if( $currentpage > $total_pages )
2560      { // current page number is greater then all page number, set current page to the last existing page
2561          $currentpage = $total_pages;
2562      }
2563  
2564      // Set first page
2565      if( $currentpage <= intval( $params['list_span'] / 2 ) )
2566      { // the current page number is small
2567          $first_page = 1;
2568      }
2569      elseif( $currentpage > $total_pages - intval( $params['list_span'] / 2 ) )
2570      { // the current page number is big
2571          $first_page = max( 1, $total_pages - $params['list_span'] + 1 );
2572      }
2573      else
2574      { // the current page number can be centered
2575          $first_page = $currentpage - intval( $params['list_span'] / 2 );
2576      }
2577  
2578      // Set last page
2579      if( $currentpage > $total_pages - intval( $params['list_span'] / 2 ) )
2580      { // the current page number is big
2581          $last_page = $total_pages;
2582      }
2583      else
2584      {
2585          $last_page = min( $total_pages, $first_page + $params['list_span'] - 1 );
2586      }
2587  
2588      echo '<div id="currentpage" value='.$currentpage.' /></div>';
2589      echo $params['list_start'];
2590      if( $comments_number > 0 )
2591      {
2592          echo $params['pages_text'];
2593          if( $currentpage > 1 )
2594          { // link to previous page
2595              echo_pagenumber( $item_ID, $params['prev_text'], $currentpage - 1 );
2596          }
2597          if( $first_page > 1 )
2598          { // link to first page
2599              echo_pagenumber( $item_ID, '1', '1' );
2600          }
2601          if( $first_page > 2 )
2602          { // link to previous pages
2603              $page_i = ceil( $first_page / 2 );
2604              echo_pagenumber( $item_ID, '...', $page_i );
2605          }
2606          for( $i = $first_page; $i <= $last_page; $i++ )
2607          { // Display list with pages
2608              if( $i == $currentpage )
2609              {
2610                  echo ' <strong>'.$i.'</strong>';
2611              }
2612              else
2613              {
2614                  echo_pagenumber( $item_ID, $i, $i );
2615              }
2616          }
2617          if( $last_page < $total_pages - 1 )
2618          { // link to next pages
2619              $page_i = $last_page + floor( ( $total_pages - $last_page ) / 2 );
2620              echo_pagenumber( $item_ID, '...', $page_i );
2621          }
2622          if( $last_page < $total_pages )
2623          { // link to last page
2624              echo_pagenumber( $item_ID, $total_pages, $total_pages );
2625          }
2626          if( $currentpage < $total_pages )
2627          { // link to next page
2628              echo_pagenumber( $item_ID, $params['next_text'], $currentpage + 1 );
2629          }
2630      }
2631      echo $params['list_end'];
2632  }
2633  
2634  /**
2635   * Get item edit modes
2636   *
2637   * @param integer blog ID
2638   * @param string action
2639   * @param string path to admin page
2640   * @param string tab switch params
2641   * @return array with modes
2642   */
2643  function get_item_edit_modes( $blog_ID, $action, $dispatcher, $tab_switch_params )
2644  {
2645      global $current_User;
2646  
2647      $BlogCache = & get_BlogCache();
2648      $Blog = & $BlogCache->get_by_ID( $blog_ID, false, false );
2649  
2650      $modes = array();
2651      $modes['simple'] = array(
2652          'text' => T_('Simple'),
2653          'href' => $dispatcher.'?ctrl=items&amp;action='.$action.'&amp;tab=simple&amp;'.$tab_switch_params,
2654          'onclick' => 'return b2edit_reload( document.getElementById(\'item_checkchanges\'), \''.$dispatcher.'?ctrl=items&amp;blog='.$blog_ID.'\', null, {tab:\'simple\'} );',
2655          // 'name' => 'switch_to_simple_tab_nocheckchanges', // no bozo check
2656      );
2657      $modes['expert'] = array(
2658          'text' => T_('Expert'),
2659          'href' => $dispatcher.'?ctrl=items&amp;action='.$action.'&amp;tab=expert&amp;'.$tab_switch_params,
2660          'onclick' => 'return b2edit_reload( document.getElementById(\'item_checkchanges\'), \''.$dispatcher.'?ctrl=items&amp;blog='.$blog_ID.'\', null, {tab:\'expert\'} );',
2661          // 'name' => 'switch_to_expert_tab_nocheckchanges', // no bozo check
2662      );
2663      if( $Blog->get_setting( 'in_skin_editing' ) && ( $current_User->check_perm( 'blog_post!published', 'edit', false, $Blog->ID ) || get_param( 'p' ) > 0 ) )
2664      {    // Show 'In skin' tab if Blog setting 'In-skin editing' is ON and User has a permission to publish item in this blog
2665          $mode_inskin_url = url_add_param( $Blog->get( 'url' ), 'disp=edit&amp;'.$tab_switch_params );
2666          $mode_inskin_action = get_samedomain_htsrv_url().'item_edit.php';
2667          $modes['inskin'] = array(
2668              'text' => T_('In skin'),
2669              'href' => $mode_inskin_url,
2670              'onclick' => 'return b2edit_reload( document.getElementById(\'item_checkchanges\'), \''.$mode_inskin_action.'\' );',
2671          );
2672      }
2673  
2674      return $modes;
2675  }
2676  
2677  
2678  /**
2679   * Check permission for editing of the item by current user
2680   *
2681   * @param integer post ID
2682   * @param boolean Set TRUE if we want to redirect when user cannot edit this post
2683   * @return boolean TRUE - user can edit this post
2684   */
2685  function check_item_perm_edit( $post_ID, $do_redirect = true )
2686  {
2687      global $Messages;
2688      global $Blog, $current_User;
2689  
2690      $user_can_edit = false;
2691  
2692      if( $post_ID > 0 )
2693      {    // Check permissions for editing of the current item
2694          $ItemCache = & get_ItemCache ();
2695          $edited_Item = $ItemCache->get_by_ID ( $post_ID );
2696          $user_can_edit = $current_User->check_perm( 'item_post!CURSTATUS', 'edit', false, $edited_Item );
2697          $permission_message = T_('You don\'t have permission to edit this post');
2698      }
2699      else
2700      {    // Check permissions for creating of a new item
2701          $perm_target = empty( $Blog ) ? NULL : $Blog->ID;
2702          $user_can_edit = $current_User->check_perm( 'blog_post_statuses', 'edit', false, $perm_target );
2703          $permission_message = T_('You don\'t have permission to post into this blog');
2704      }
2705  
2706      if( ! $user_can_edit )
2707      {
2708          if( $do_redirect )
2709          {    // Redirect to the blog url for users without messaging permission
2710              $Messages->add( $permission_message );
2711              if( empty( $Blog ) )
2712              {    // Bad request without blog ID
2713                  global $home_url;
2714                  $redirect_to = $home_url;
2715              }
2716              else
2717              {    // Redirect to the current blog
2718                  $redirect_to = $Blog->gen_blogurl();
2719              }
2720              header_redirect( $redirect_to, 302 );
2721              // will have exited
2722          }
2723          else
2724          {    // Current user cannot edit this post
2725              return false;
2726          }
2727      }
2728  
2729      return true;
2730  }
2731  
2732  
2733  /**
2734   * Check permission for creating of a new item by current user
2735   *
2736   * @return boolean, TRUE if user can create a new post for the current blog
2737   */
2738  function check_item_perm_create()
2739  {
2740      global $Blog;
2741  
2742      if( empty( $Blog ) )
2743      {    // Strange case, but we restrict to create a new post
2744          return false;
2745      }
2746  
2747      if( ! is_logged_in( false ) || ! $Blog->get_setting( 'in_skin_editing' ) )
2748      {    // Don't allow anonymous users to create a new post & If setting is OFF
2749          return false;
2750      }
2751      else
2752      {    // Check permissions for current user
2753          global $current_User;
2754          return $current_User->check_perm( 'blog_post_statuses', 'edit', false, $Blog->ID );
2755      }
2756  
2757      return true;
2758  }
2759  
2760  
2761  /**
2762   * Template tag: Display a link to create a new post
2763   */
2764  function item_new_link( $before = '', $after = '', $link_text = '', $link_title = '#' )
2765  {
2766      echo get_item_new_link( $before, $after, $link_text, $link_title );
2767  }
2768  
2769  
2770  /**
2771   * Template tag: Get a link to create a new post
2772   *
2773   * @return string|false
2774   */
2775  function get_item_new_link( $before = '', $after = '', $link_text = '', $link_title = '#' )
2776  {
2777      global $Blog;
2778  
2779      if( ! check_item_perm_create() )
2780      {    // Don't allow users to create a new post
2781          return false;
2782      }
2783  
2784      if( $link_text == '' )
2785      {    // Default text
2786          $link_text = T_('Write a new post');
2787      }
2788  
2789      if( $link_title == '#' ) $link_title = T_('Write a new post');
2790  
2791      $r = $before
2792          .'<a href="'.url_add_param( $Blog->get( 'url' ), 'disp=edit' ).'" title="'.$link_title.'">'
2793          .$link_text
2794          .'</a>'
2795          .$after;
2796  
2797      return $r;
2798  }
2799  
2800  
2801  /**
2802   * Display location select elements (Country, Region, Subregion, City)
2803   *
2804   * @param object Form
2805   * @param object Edited Item
2806   */
2807  function echo_item_location_form( & $Form, & $edited_Item )
2808  {
2809      load_class( 'regional/model/_country.class.php', 'Country' );
2810      load_funcs( 'regional/model/_regional.funcs.php' );
2811  
2812      $edited_Item_Blog = $edited_Item->get_Blog();
2813      if( !$edited_Item_Blog->country_visible() )
2814      {    // If country is NOT visible it means all other location fields also are not visible, so exit here
2815          return;
2816      }
2817  
2818      global $rsc_url;
2819  
2820      $Form->begin_fieldset( T_('Location') );
2821  
2822      $table_class = '';
2823      if( $edited_Item->is_special() )
2824      { // A post with special type should always has the location fields as not required
2825          // This css class hides all red stars of required fields
2826          $table_class = ' not_required';
2827      }
2828  
2829      $Form->switch_layout( 'table' );
2830      $Form->formstart = '<table id="item_locations" cellspacing="0" class="fform'.$table_class.'">'."\n";
2831      $Form->labelstart = '<td class="right"><strong>';
2832      $Form->labelend = '</strong></td>';
2833  
2834      echo $Form->formstart;
2835  
2836      $button_refresh_regional = '<button id="%s" type="submit" name="actionArray[edit_switchtab]" class="action_icon refresh_button">'.get_icon( 'refresh' ).'</button>';
2837      $button_refresh_regional .= '<img src="'.$rsc_url.'img/ajax-loader.gif" alt="'.T_('Loading...').'" title="'.T_('Loading...').'" style="display:none;margin-left:5px" align="top" />';
2838  
2839      // Country
2840      $CountryCache = & get_CountryCache();
2841      $country_is_required = ( $edited_Item->Blog->get_setting( 'location_country' ) == 'required' );
2842      $Form->select_country( 'item_ctry_ID', $edited_Item->ctry_ID, $CountryCache, T_('Country'), array( 'required' => $country_is_required, 'allow_none' => true ) );
2843  
2844      if( $edited_Item->Blog->region_visible() )
2845      { // Region
2846          $region_is_required = ( $edited_Item->Blog->get_setting( 'location_region' ) == 'required' );
2847          $Form->select_input_options( 'item_rgn_ID', get_regions_option_list( $edited_Item->ctry_ID, $edited_Item->rgn_ID, array( 'none_option_text' => T_( 'Unknown' ) ) ), T_( 'Region' ), sprintf( $button_refresh_regional, 'button_refresh_region' ), array( 'required' => $region_is_required ) );
2848      }
2849  
2850      if( $edited_Item->Blog->subregion_visible() )
2851      { // Subregion
2852          $subregion_is_required = ( $edited_Item->Blog->get_setting( 'location_subregion' ) == 'required' );
2853          $Form->select_input_options( 'item_subrg_ID', get_subregions_option_list( $edited_Item->rgn_ID, $edited_Item->subrg_ID, array( 'none_option_text' => T_( 'Unknown' ) ) ), T_( 'Sub-region' ), sprintf( $button_refresh_regional, 'button_refresh_subregion' ), array( 'required' => $subregion_is_required ) );
2854      }
2855  
2856      if( $edited_Item->Blog->city_visible() )
2857      { // City
2858          $city_is_required = ( $edited_Item->Blog->get_setting( 'location_city' ) == 'required' );
2859          $Form->select_input_options( 'item_city_ID', get_cities_option_list( $edited_Item->ctry_ID, $edited_Item->rgn_ID, $edited_Item->subrg_ID, $edited_Item->city_ID, array( 'none_option_text' => T_( 'Unknown' ) ) ), T_( 'City' ), sprintf( $button_refresh_regional, 'button_refresh_city' ), array( 'required' => $city_is_required ) );
2860      }
2861  
2862      echo $Form->formend;
2863  
2864      $Form->switch_layout( NULL );
2865  
2866      $Form->end_fieldset();
2867  }
2868  
2869  
2870  /**
2871   * Get custom fields for item of current Blog
2872   *
2873   * @return array Custom fields = array( 'name', 'type', 'title' )
2874   */
2875  function get_item_custom_fields()
2876  {
2877      global $Blog;
2878  
2879      $custom_fields = array();
2880  
2881      if( empty( $Blog ) )
2882      {    // No Blog
2883          return $custom_fields;
2884      }
2885  
2886      foreach( array( 'double', 'varchar' ) as $type )
2887      { // get all types of custom fields
2888          $count_custom_field = $Blog->get_setting( 'count_custom_'.$type );
2889          for( $i = 1; $i <= $count_custom_field; $i++ )
2890          { // Add each custom field with type $type to the custom_fields array
2891              $field_guid = $Blog->get_setting( 'custom_'.$type.$i );
2892              $field_type_guid = $type.'_'.$field_guid;
2893              $field_title = $Blog->get_setting( 'custom_'.$field_type_guid );
2894              $field_fname = $Blog->get_setting( 'custom_fname_'.$field_guid );
2895              if( $field_title && $field_fname )
2896              {
2897                  $field_index = preg_replace( '/\s+/', '_', strtolower( trim( $field_fname ) ) );
2898                  $custom_fields[$field_index] = array(
2899                          'name' => $field_type_guid,
2900                          'type' => $type,
2901                          'title' => $field_title
2902                      );
2903              }
2904          }
2905      }
2906  
2907      return $custom_fields;
2908  }
2909  
2910  
2911  /**
2912   * Display custom field settings as hidden input values
2913   *
2914   * @param object Form
2915   * @param object edited Item
2916   */
2917  function display_hidden_custom_fields( & $Form, & $edited_Item )
2918  {
2919      $edited_Item->load_Blog();
2920  
2921      foreach( array( 'double', 'varchar' ) as $type )
2922      { // get all types of custom fields
2923          $count_custom_field = $edited_Item->Blog->get_setting( 'count_custom_'.$type );
2924          for( $i = 1; $i <= $count_custom_field; $i++ )
2925          { // For each custom field with type $type:
2926              $field_guid = $edited_Item->Blog->get_setting( 'custom_'.$type.$i );
2927              $Form->hidden( 'item_'.$type.'_'.$field_guid, $edited_Item->get_setting( 'custom_'.$type.'_'.$field_guid ) );
2928          }
2929      }
2930  }
2931  
2932  
2933  /**
2934   * Log a creating of new item (Increase counter in global cache)
2935   *
2936   * @param string Source of item creation ( 'through_admin', 'through_xmlrpc', 'through_email' )
2937   */
2938  function log_new_item_create( $created_through )
2939  {
2940      /**
2941       * @var AbstractSettings
2942       */
2943      global $global_Cache;
2944  
2945      if( empty( $global_Cache ) )
2946      {    // Init global cache if it is not defined (for example, during on install process)
2947          $global_Cache = new AbstractSettings( 'T_global__cache', array( 'cach_name' ), 'cach_cache', 0 /* load all */ );
2948      }
2949  
2950      if( !in_array( $created_through, array( 'through_admin', 'through_xmlrpc', 'through_email' ) ) )
2951      {    // Set default value if source is wrong
2952          $created_through = 'through_admin';
2953      }
2954  
2955      // Set variable name for current post counter
2956      $cache_var_name = 'post_'.$created_through;
2957  
2958      // Get previuos counter value
2959      $counter = (int) $global_Cache->get( $cache_var_name );
2960  
2961      // Increase counter
2962      $global_Cache->set( $cache_var_name, $counter + 1 );
2963  
2964      // Update the changed data in global cache
2965      $global_Cache->dbupdate();
2966  }
2967  
2968  
2969  /**
2970   * Display the manual pages results table
2971   *
2972   * @param array Params
2973   */
2974  function items_manual_results_block( $params = array() )
2975  {
2976      // Make sure we are not missing any param:
2977      $params = array_merge( array(
2978              'results_param_prefix' => 'items_manual_',
2979          ), $params );
2980  
2981      if( !is_logged_in() )
2982      { // Only logged in users can access to this function
2983          return;
2984      }
2985  
2986      global $current_User, $blog, $Blog, $admin_url, $Session;
2987  
2988      $result_fadeout = $Session->get( 'fadeout_array' );
2989  
2990      $cat_ID = param( 'cat_ID', 'integer', 0 );
2991  
2992      if( empty( $Blog ) )
2993      { // Init Blog
2994          $BlogCache = & get_BlogCache();
2995          $blog = get_param( 'blog' );
2996          if( !empty( $blog ) )
2997          { // Get Blog by ID
2998              $Blog = $BlogCache->get_by_ID( $blog, false );
2999          }
3000          if( empty( $Blog ) && !empty( $cat_ID ) )
3001          { // Get Blog from chapter ID
3002              $ChapterCache = & get_ChapterCache();
3003              if( $Chapter = & $ChapterCache->get_by_ID( $cat_ID, false ) )
3004              {
3005                  $Blog = $Chapter->get_Blog();
3006                  $blog = $Blog->ID;
3007              }
3008          }
3009      }
3010  
3011      if( empty( $Blog ) || $Blog->get( 'type' ) != 'manual' )
3012      { // No Blog, Exit here
3013          return;
3014      }
3015  
3016      if( is_ajax_content() )
3017      {
3018          $order_action = param( 'order_action', 'string' );
3019  
3020          if( $order_action == 'update' )
3021          { // Update an order to new value
3022              $order_value = (int)param( 'order_value', 'string', 0 );
3023              $order_data = param( 'order_data', 'string' );
3024              $order_data = explode( '-', $order_data );
3025              $order_obj_ID = (int)$order_data[2];
3026              if( $order_obj_ID > 0 )
3027              {
3028                  switch( $order_data[1] )
3029                  {
3030                      case 'chapter':
3031                          // Update chapter order
3032                          $ChapterCache = & get_ChapterCache();
3033                          if( $updated_Chapter = & $ChapterCache->get_by_ID( $order_obj_ID, false ) )
3034                          {
3035                              if( $current_User->check_perm( 'blog_cats', '', false, $updated_Chapter->blog_ID ) )
3036                              { // Check permission to edit this Chapter
3037                                  $updated_Chapter->set( 'order', $order_value );
3038                                  $updated_Chapter->dbupdate();
3039                                  $ChapterCache->clear();
3040                              }
3041                          }
3042                          break;
3043  
3044                      case 'item':
3045                          // Update item order
3046                          $ItemCache = & get_ItemCache();
3047                          if( $updated_Item = & $ItemCache->get_by_ID( $order_obj_ID, false ) )
3048                          {
3049                              if( $current_User->check_perm( 'item_post!CURSTATUS', 'edit', false, $updated_Item ) )
3050                              { // Check permission to edit this Item
3051                                  $updated_Item->set( 'order', $order_value );
3052                                  $updated_Item->dbupdate();
3053                              }
3054                          }
3055                          break;
3056                  }
3057              }
3058          }
3059      }
3060  
3061      load_class( '_core/ui/_uiwidget.class.php', 'Table' );
3062  
3063      $Table = new Table( NULL, $params['results_param_prefix'] );
3064  
3065      $Table->title = T_('Manual Pages');
3066  
3067      // Redirect to manual pages after adding chapter
3068      $redirect_page = '&amp;redirect_page=manual';
3069      $Table->global_icon( T_('Add new chapter...'), 'add', $admin_url.'?ctrl=chapters&amp;action=new&amp;blog='.$blog.$redirect_page, ' '.T_('Add top level chapter').' &raquo;', 3, 4 );
3070  
3071      $Table->cols[] = array(
3072                              'th' => T_('Name'),
3073                          );
3074      $Table->cols[] = array(
3075                              'th' => T_('URL "slug"'),
3076                          );
3077      $Table->cols[] = array(
3078                              'th' => T_('Order'),
3079                              'th_class' => 'shrinkwrap',
3080                          );
3081      $Table->cols[] = array(
3082                              'th' => T_('Actions'),
3083                          );
3084  
3085      if( is_ajax_content() )
3086      { // init results param by template name
3087          if( !isset( $params[ 'skin_type' ] ) || ! isset( $params[ 'skin_name' ] ) )
3088          {
3089              debug_die( 'Invalid ajax results request!' );
3090          }
3091          $Table->init_params_by_skin( $params[ 'skin_type' ], $params[ 'skin_name' ] );
3092      }
3093  
3094      $Table->display_init( NULL, $result_fadeout );
3095  
3096      $Table->display_head();
3097  
3098      echo $Table->replace_vars( $Table->params['content_start'] );
3099  
3100      $Table->display_list_start();
3101  
3102          $Table->display_col_headers();
3103  
3104          $Table->display_body_start();
3105  
3106          manual_display_chapters();
3107  
3108          $Table->display_body_end();
3109  
3110      $Table->display_list_end();
3111  
3112      // Flush fadeout
3113      $Session->delete( 'fadeout_array');
3114  
3115      echo $Table->params['content_end'];
3116  
3117      if( !is_ajax_content() )
3118      { // Create this hidden div to get a function name for AJAX request
3119          echo '<div id="'.$params['results_param_prefix'].'ajax_callback" style="display:none">'.__FUNCTION__.'</div>';
3120      }
3121  }
3122  
3123  
3124  /**
3125   * Display the created items results table
3126   *
3127   * @param array Params
3128   */
3129  function items_created_results_block( $params = array() )
3130  {
3131      // Make sure we are not missing any param:
3132      $params = array_merge( array(
3133              'edited_User'          => NULL,
3134              'results_param_prefix' => 'actv_postown_',
3135              'results_title'        => T_('Posts created by the user'),
3136              'results_no_text'      => T_('User has not created any posts'),
3137          ), $params );
3138  
3139      if( !is_logged_in() )
3140      {    // Only logged in users can access to this function
3141          return;
3142      }
3143  
3144      global $current_User;
3145      if( !$current_User->check_perm( 'users', 'edit' ) )
3146      {    // Check minimum permission:
3147          return;
3148      }
3149  
3150      $edited_User = $params['edited_User'];
3151      if( !$edited_User )
3152      {    // No defined User, probably the function is calling from AJAX request
3153          $user_ID = param( 'user_ID', 'integer', 0 );
3154          if( empty( $user_ID ) )
3155          {    // Bad request, Exit here
3156              return;
3157          }
3158          $UserCache = & get_UserCache();
3159          if( ( $edited_User = & $UserCache->get_by_ID( $user_ID, false ) ) === false )
3160          {    // Bad request, Exit here
3161              return;
3162          }
3163      }
3164  
3165      global $DB;
3166  
3167      param( 'user_tab', 'string', '', true );
3168      param( 'user_ID', 'integer', 0, true );
3169  
3170      $SQL = new SQL();
3171      $SQL->SELECT( '*' );
3172      $SQL->FROM( 'T_items__item' );
3173      $SQL->WHERE( 'post_creator_user_ID = '.$DB->quote( $edited_User->ID ) );
3174  
3175      // Create result set:
3176      $created_items_Results = new Results( $SQL->get(), $params['results_param_prefix'], 'D' );
3177      $created_items_Results->Cache = & get_ItemCache();
3178      $created_items_Results->title = $params['results_title'];
3179      $created_items_Results->no_results_text = $params['results_no_text'];
3180  
3181      // Get a count of the post which current user can delete
3182      $deleted_posts_created_count = count( $edited_User->get_deleted_posts( 'created' ) );
3183      if( $created_items_Results->total_rows > 0 && $deleted_posts_created_count > 0 )
3184      {    // Display action icon to delete all records if at least one record exists & current user can delete at least one item created by user
3185          $created_items_Results->global_icon( sprintf( T_('Delete all post created by %s'), $edited_User->login ), 'delete', '?ctrl=user&amp;user_tab=activity&amp;action=delete_all_posts_created&amp;user_ID='.$edited_User->ID.'&amp;'.url_crumb('user'), ' '.T_('Delete all'), 3, 4 );
3186      }
3187  
3188      // Initialize Results object
3189      items_results( $created_items_Results, array(
3190              'field_prefix' => 'post_',
3191              'display_ord' => false,
3192              'display_history' => false,
3193          ) );
3194  
3195      $display_params = array(
3196          'before' => '<div class="results" style="margin-top:25px" id="created_posts_result">'
3197      );
3198  
3199      if( is_ajax_content() )
3200      { // init results param by template name
3201          if( !isset( $params[ 'skin_type' ] ) || ! isset( $params[ 'skin_name' ] ) )
3202          {
3203              debug_die( 'Invalid ajax results request!' );
3204          }
3205          $created_items_Results->init_params_by_skin( $params[ 'skin_type' ], $params[ 'skin_name' ] );
3206      }
3207  
3208      $created_items_Results->display( $display_params );
3209  
3210      if( !is_ajax_content() )
3211      {    // Create this hidden div to get a function name for AJAX request
3212          echo '<div id="'.$params['results_param_prefix'].'ajax_callback" style="display:none">'.__FUNCTION__.'</div>';
3213      }
3214  }
3215  
3216  
3217  /**
3218   * Display the edited items results table
3219   *
3220   * @param array Params
3221   */
3222  function items_edited_results_block( $params = array() )
3223  {
3224      // Make sure we are not missing any param:
3225      $params = array_merge( array(
3226              'edited_User'          => NULL,
3227              'results_param_prefix' => 'actv_postedit_',
3228              'results_title'        => T_('Posts edited by the user'),
3229              'results_no_text'      => T_('User has not edited any posts'),
3230          ), $params );
3231  
3232      if( !is_logged_in() )
3233      {    // Only logged in users can access to this function
3234          return;
3235      }
3236  
3237      global $current_User;
3238      if( !$current_User->check_perm( 'users', 'edit' ) )
3239      {    // Check minimum permission:
3240          return;
3241      }
3242  
3243      $edited_User = $params['edited_User'];
3244      if( !$edited_User )
3245      {    // No defined User, probably the function is calling from AJAX request
3246          $user_ID = param( 'user_ID', 'integer', 0 );
3247          if( empty( $user_ID ) )
3248          {    // Bad request, Exit here
3249              return;
3250          }
3251          $UserCache = & get_UserCache();
3252          if( ( $edited_User = & $UserCache->get_by_ID( $user_ID, false ) ) === false )
3253          {    // Bad request, Exit here
3254              return;
3255          }
3256      }
3257  
3258      global $DB;
3259  
3260      param( 'user_tab', 'string', '', true );
3261      param( 'user_ID', 'integer', 0, true );
3262  
3263      $edited_versions_SQL = new SQL();
3264      $edited_versions_SQL->SELECT( 'DISTINCT( iver_itm_ID )' );
3265      $edited_versions_SQL->FROM( 'T_items__version' );
3266      $edited_versions_SQL->WHERE( 'iver_edit_user_ID = '.$DB->quote( $edited_User->ID ) );
3267  
3268      $SQL = new SQL();
3269      $SQL->SELECT( '*' );
3270      $SQL->FROM( 'T_items__item ' );
3271      $SQL->WHERE( '( ( post_lastedit_user_ID = '.$DB->quote( $edited_User->ID ).' ) OR ( post_ID IN ( '.$edited_versions_SQL->get().' ) ) )' );
3272      $SQL->WHERE_and( 'post_creator_user_ID != '.$DB->quote( $edited_User->ID ) );
3273  
3274      // Create result set:
3275      $edited_items_Results = new Results( $SQL->get(), $params['results_param_prefix'], 'D' );
3276      $edited_items_Results->Cache = & get_ItemCache();
3277      $edited_items_Results->title = $params['results_title'];
3278      $edited_items_Results->no_results_text = $params['results_no_text'];
3279  
3280      // Get a count of the post which current user can delete
3281      $deleted_posts_edited_count = count( $edited_User->get_deleted_posts( 'edited' ) );
3282      if( $edited_items_Results->total_rows > 0 && $deleted_posts_edited_count > 0 )
3283      {    // Display actino icon to delete all records if at least one record exists & current user can delete at least one item created by user
3284          $edited_items_Results->global_icon( sprintf( T_('Delete all post edited by %s'), $edited_User->login ), 'delete', '?ctrl=user&amp;user_tab=activity&amp;action=delete_all_posts_edited&amp;user_ID='.$edited_User->ID.'&amp;'.url_crumb('user'), ' '.T_('Delete all'), 3, 4 );
3285      }
3286  
3287      // Initialize Results object
3288      items_results( $edited_items_Results, array(
3289              'field_prefix' => 'post_',
3290              'display_ord' => false,
3291              'display_history' => false,
3292          ) );
3293  
3294      if( is_ajax_content() )
3295      { // init results param by template name
3296          if( !isset( $params[ 'skin_type' ] ) || ! isset( $params[ 'skin_name' ] ) )
3297          {
3298              debug_die( 'Invalid ajax results request!' );
3299          }
3300          $edited_items_Results->init_params_by_skin( $params[ 'skin_type' ], $params[ 'skin_name' ] );
3301      }
3302  
3303      $display_params = array(
3304          'before' => '<div class="results" style="margin-top:25px" id="edited_posts_result">'
3305      );
3306      $edited_items_Results->display( $display_params );
3307  
3308      if( !is_ajax_content() )
3309      {    // Create this hidden div to get a function name for AJAX request
3310          echo '<div id="'.$params['results_param_prefix'].'ajax_callback" style="display:none">'.__FUNCTION__.'</div>';
3311      }
3312  }
3313  
3314  
3315  /**
3316   * Display the items list (Used to load next page of the items by AJAX)
3317   *
3318   * @param array Params
3319   */
3320  function items_list_block_by_page( $params = array() )
3321  {
3322      $params = array_merge( array(
3323              'skin_name'    => '',
3324              'content_mode' => 'auto', // 'auto' will auto select depending on $disp-detail
3325              'image_size'   => 'fit-400x320',
3326              'block_start'  => '<div class="navigation ajax">',
3327              'block_end'    => '</div>',
3328              'links_format' => '$next$',
3329              'next_text'    => T_('Load more entries').'&hellip;',
3330          ), $params );
3331  
3332      if( !skin_init_ajax( $params['skin_name'], 'posts' ) )
3333      {    // Exit here if skin cannot be initialized
3334          return;
3335      }
3336  
3337      while( $Item = & mainlist_get_item() )
3338      {    // For each blog post:
3339          // ---------------------- ITEM BLOCK INCLUDED HERE ------------------------
3340          skin_include( '_item_block.inc.php', $params );
3341          // ----------------------------END ITEM BLOCK  ----------------------------
3342      }
3343  
3344      // -------------------- PREV/NEXT PAGE LINKS (POST LIST MODE) --------------------
3345      mainlist_page_links( $params );
3346      // ------------------------- END OF PREV/NEXT PAGE LINKS -------------------------
3347  }
3348  
3349  
3350  /**
3351   * Initialize Results object for items list
3352   *
3353   * @param object Results
3354   * @param array Params
3355   */
3356  function items_results( & $items_Results, $params = array() )
3357  {
3358      global $Blog;
3359  
3360      // Make sure we are not missing any param:
3361      $params = array_merge( array(
3362              'tab' => '',
3363              'field_prefix' => '',
3364              'display_date' => true,
3365              'display_permalink' => true,
3366              'display_blog' => true,
3367              'display_type' => true,
3368              'display_author' => true,
3369              'display_title' => true,
3370              'display_title_flag' => true,
3371              'display_title_status' => true,
3372              'display_visibility_actions' => true,
3373              'display_ord' => true,
3374              'display_history' => true,
3375              'display_actions' => true,
3376          ), $params );
3377  
3378      if( $params['display_date'] )
3379      {    // Display Date column
3380          $td = '<span class="date">@get_issue_date()@</span>';
3381          if( $params['display_permalink'] )
3382          {
3383              $td = '@get_permanent_link( get_icon(\'permalink\'), \'\', \'\', \'auto\' )@ '.$td;
3384          }
3385          $items_Results->cols[] = array(
3386                  'th' => T_('Date'),
3387                  'order' => $params['field_prefix'].'datestart',
3388                  'default_dir' => 'D',
3389                  'th_class' => 'shrinkwrap',
3390                  'td_class' => 'shrinkwrap',
3391                  'td' => $td,
3392              );
3393      }
3394  
3395      if( $params['display_blog'] )
3396      {    // Display Blog column
3397          if( !empty( $Blog ) && $Blog->get_setting( 'aggregate_coll_IDs' ) )
3398          { // Aggregated blog: display name of blog
3399              $items_Results->cols[] = array(
3400                      'th' => T_('Blog'),
3401                      'th_class' => 'nowrap',
3402                      'td_class' => 'nowrap',
3403                      'td' => '@load_Blog()@<a href="~regenerate_url( \'blog,results_order\', \'blog=@blog_ID@\' )~">@Blog->dget(\'shortname\')@</a>',
3404                  );
3405          }
3406      }
3407  
3408      if( $params['tab'] == 'intros' && $params['display_type'] )
3409      { // Display Type column:
3410          $items_Results->cols[] = array(
3411                  'th' => T_('Type'),
3412                  'th_class' => 'nowrap',
3413                  'td_class' => 'nowrap',
3414                  'order' => $params['field_prefix'].'ptyp_ID',
3415                  'td' => '@type()@',
3416              );
3417      }
3418      else if( $params['display_author'] )
3419      { // Display Author column:
3420          $items_Results->cols[] = array(
3421                  'th' => T_('Author'),
3422                  'th_class' => 'nowrap',
3423                  'td_class' => 'nowrap',
3424                  'order' => $params['field_prefix'].'creator_user_ID',
3425                  'td' => '%get_user_identity_link( NULL, #post_creator_user_ID# )%',
3426              );
3427      }
3428  
3429      if( $params['display_title'] )
3430      { // Display Title column
3431          $items_Results->cols[] = array(
3432                  'th' => T_('Title'),
3433                  'order' => $params['field_prefix'].'title',
3434                  'td_class' => 'tskst_$post_pst_ID$',
3435                  'td' => '<strong lang="@get(\'locale\')@">%task_title_link( {Obj}, '.(int)$params['display_title_flag'].', '.(int)$params['display_title_status'].' )%</strong>',
3436              );
3437      }
3438  
3439      if( $params['display_visibility_actions'] )
3440      { // Display Visibility actions
3441          $items_Results->cols[] = array(
3442                  'th' => T_('Title'),
3443                  'td_class' => 'shrinkwrap',
3444                  'td' => '%item_visibility( {Obj} )%',
3445              );
3446      }
3447  
3448      if( $params['display_ord'] )
3449      {    // Display Ord column
3450          $items_Results->cols[] = array(
3451                  'th' => T_('Ord'),
3452                  'order' => $params['field_prefix'].'order',
3453                  'td_class' => 'right',
3454                  'td' => '$post_order$',
3455              );
3456      }
3457  
3458      if( $params['display_history'] )
3459      {    // Display History (i) column
3460          $items_Results->cols[] = array(
3461                  'th' => /* TRANS: abbrev for info */ T_('i'),
3462                  'th_title' => T_('Item history information'),
3463                  'order' => $params['field_prefix'].'datemodified',
3464                  'default_dir' => 'D',
3465                  'th_class' => 'shrinkwrap',
3466                  'td_class' => 'shrinkwrap',
3467                  'td' => '<a href="?ctrl=items&amp;p=$post_ID$&amp;action=history">@history_info_icon()@</a>',
3468              );
3469      }
3470  
3471      if( $params['display_actions'] )
3472      {    // Display Actions column
3473          $items_Results->cols[] = array(
3474                  'th' => T_('Actions'),
3475                  'td_class' => 'shrinkwrap',
3476                  'td' => '%item_edit_actions( {Obj} )%',
3477              );
3478      }
3479  }
3480  
3481  
3482  /**
3483   * Helper functions to display Items results.
3484   * New ( not display helper ) functions must be created above item_results function
3485   */
3486  
3487  /**
3488   * Get a link with task title
3489   *
3490   * @param object Item
3491   * @param boolean Display country flag
3492   * @param boolean Display status banner
3493   * @return string Link
3494   */
3495  function task_title_link( $Item, $display_flag = true, $display_status = false )
3496  {
3497      global $current_User, $admin_url;
3498  
3499      $col = '';
3500      if( $display_status && is_logged_in() )
3501      { // Display status
3502          $col .= $Item->get_status( array( 'format' => 'styled' ) );
3503      }
3504  
3505      if( $display_flag )
3506      { // Display country flag
3507          $col .= locale_flag( $Item->locale, 'w16px', 'flag', '', false ).' ';
3508      }
3509  
3510      $Item->get_Blog();
3511  
3512      if( is_admin_page() )
3513      { // Url to item page in backoffice
3514          $item_url = $admin_url.'?ctrl=items&amp;blog='.$Item->get_blog_ID().'&amp;p='.$Item->ID;
3515      }
3516      else
3517      { // Url to item page in frontoffice
3518          $item_url = $Item->get_permanent_url();
3519      }
3520  
3521      if( $Item->Blog->get_setting( 'allow_comments' ) != 'never' )
3522      { // The current blog can have comments:
3523          $nb_comments = generic_ctp_number( $Item->ID, 'feedback' );
3524          $comments_url = is_admin_page() ? $item_url : url_add_tail( $item_url, '#comments' );
3525          $col .= '<a href="'.$comments_url.'" title="'.sprintf( T_('%d feedbacks'), $nb_comments ).'" class="">';
3526          if( $nb_comments )
3527          {
3528              $col .= get_icon( 'comments' );
3529          }
3530          else
3531          {
3532              $col .= get_icon( 'nocomment' );
3533          }
3534          $col .= '</a> ';
3535      }
3536  
3537      $col .= '<a href="'.$item_url.'" class="" title="'.
3538                                  T_('View this post...').'">'.$Item->dget( 'title' ).'</a></strong>';
3539  
3540      return $col;
3541  }
3542  
3543  /**
3544   * Get the icons to publish or to deprecate the item
3545   *
3546   * @param object Item
3547   * @return string Action icons
3548   */
3549  function item_visibility( $Item )
3550  {
3551      // Display publish NOW button if current user has the rights:
3552      $r = $Item->get_publish_link( ' ', ' ', get_icon( 'publish' ), '#', '' );
3553  
3554      // Display deprecate if current user has the rights:
3555      $r .= $Item->get_deprecate_link( ' ', ' ', get_icon( 'deprecate' ), '#', '' );
3556  
3557      if( empty( $r ) )
3558      {    // for IE
3559          $r = '&nbsp;';
3560      }
3561  
3562      return $r;
3563  }
3564  
3565  /**
3566   * Edit Actions:
3567   *
3568   * @param Item
3569   */
3570  function item_edit_actions( $Item )
3571  {
3572      $r = '';
3573  
3574      if( isset($GLOBALS['files_Module']) )
3575      {
3576          $r .= action_icon( T_('Edit linked files...'), 'folder',
3577                      url_add_param( $Item->get_Blog()->get_filemanager_link(), 'fm_mode=link_object&amp;link_type=item&amp;link_object_ID='.$Item->ID ), T_('Files') );
3578      }
3579  
3580      // Display edit button if current user has the rights:
3581      $r .= $Item->get_edit_link( array(
3582          'before' => ' ',
3583          'after' => ' ',
3584          'text' => get_icon( 'edit' ),
3585          'title' => '#',
3586          'class' => '' ) );
3587  
3588      // Display duplicate button if current user has the rights:
3589      $r .= $Item->get_copy_link( array(
3590          'before' => ' ',
3591          'after' => ' ',
3592          'text' => get_icon( 'copy', 'imgtag', array( 'title' => T_('Duplicate this post...') ) ),
3593          'title' => '#',
3594          'class' => '' ) );
3595  
3596      // Display delete button if current user has the rights:
3597      $r .= $Item->get_delete_link( ' ', ' ', get_icon( 'delete' ), '#', '', false, '#', '#', regenerate_url( '', '', '', '&' ) );
3598  
3599      return $r;
3600  }
3601  
3602  
3603  /**
3604   * Display chapters list
3605   *
3606   * @param array Params
3607   */
3608  function manual_display_chapters( $params = array(), $level = 0 )
3609  {
3610      $params = array_merge( array(
3611              'parent_cat_ID'      => 0,
3612          ), $params );
3613  
3614      global $Blog, $blog, $cat_ID;
3615  
3616      $chapters = manual_get_chapters( (int)$params['parent_cat_ID'] );
3617  
3618      if( empty( $chapters ) )
3619      { // No categories, Exit here
3620          return;
3621      }
3622  
3623      if( empty( $Blog ) && !empty( $blog ) )
3624      { // Set Blog if it still doesn't exist
3625          $BlogCache = & get_BlogCache();
3626          $Blog = & $BlogCache->get_by_ID( $blog, false );
3627      }
3628  
3629      if( empty( $Blog ) )
3630      { // No Blog, Exit here
3631          return;
3632      }
3633  
3634      $chapter_path = array();
3635      if( !empty( $cat_ID ) )
3636      { // A category is opened
3637          $chapter_path = manual_get_chapter_path( $cat_ID );
3638      }
3639      //pre_dump($chapter_path);
3640  
3641      foreach( $chapters as $Chapter )
3642      { // Display all given chapters
3643          manual_display_chapter( array_merge( $params, array(
3644                  'Chapter'      => $Chapter,
3645                  'chapter_path' => $chapter_path,
3646              ) ), $level );
3647      }
3648  }
3649  
3650  
3651  /**
3652   * Display chapter and children
3653   *
3654   * @param array Params
3655   * @param integer Level of the category in the recursive tree
3656   */
3657  function manual_display_chapter( $params = array(), $level = 0 )
3658  {
3659      $params = array_merge( array(
3660              'Chapter'      => NULL,
3661              'chapter_path' => array(),
3662          ), $params );
3663  
3664      global $Blog;
3665  
3666      if( empty( $params['Chapter'] ) )
3667      { // No Chapter, Exit here
3668          return;
3669      }
3670  
3671      $Chapter = & $params['Chapter'];
3672  
3673      $is_selected = false;
3674      $is_opened = false;
3675  
3676      $classes = array();
3677      if( !empty( $params['chapter_path'] ) && in_array( $Chapter->ID, $params['chapter_path'] ) )
3678      { // A category is selected
3679          $is_selected = true;
3680      }
3681      if( !empty( $Chapter->children ) && $is_selected )
3682      { // A category is opened
3683          $is_opened = true;
3684      }
3685      else if( $Chapter->has_posts() && $is_selected )
3686      { // A category is selected and it has the posts
3687          $is_opened = true;
3688      }
3689  
3690      manual_display_chapter_row( $Chapter, $level, $is_opened );
3691  
3692      if( $is_selected )
3693      {
3694          global $Settings;
3695  
3696          if( $Settings->get( 'chapter_ordering' ) == 'manual' &&
3697                  $Blog->get_setting( 'orderby' ) == 'order' &&
3698                  $Blog->get_setting( 'orderdir' ) == 'ASC' )
3699          { // Items & categories are ordered by manual field 'order'
3700              // In this mode we should show them in one merged list ordered by field 'order'
3701              $chapters_items_mode = 'order';
3702          }
3703          else
3704          { // Standard mode for all other cases
3705              $chapters_items_mode = 'std';
3706          }
3707  
3708          if( $chapters_items_mode != 'order' )
3709          { // Display all subchapters
3710              manual_display_chapters( array_merge( $params, array(
3711                      'parent_cat_ID'      => $Chapter->ID,
3712                  ) ), $level + 1 );
3713          }
3714  
3715          if( $Chapter->has_posts() || $is_opened )
3716          { // Display the posts/subcategories of this chapter
3717              manual_display_posts( array_merge( $params, array(
3718                  'chapter_ID'          => $Chapter->ID,
3719                  'chapters_items_mode' => $chapters_items_mode,
3720              ) ), $level + 1 );
3721          }
3722      }
3723  }
3724  
3725  
3726  /**
3727   * Display a list of the posts for current chapter
3728   *
3729   * @param array params
3730   * @return string List with posts
3731   */
3732  function manual_display_posts( $params = array(), $level = 0 )
3733  {
3734      $params = array_merge( array(
3735              'chapter_ID'          => 0,
3736              'chapters_items_mode' => 'std',
3737          ), $params );
3738  
3739      global $DB, $Blog, $blog;
3740  
3741      if( empty( $Blog ) && !empty( $blog ) )
3742      { // Set Blog if it still doesn't exist
3743          $BlogCache = & get_BlogCache();
3744          $Blog = & $BlogCache->get_by_ID( $blog, false );
3745      }
3746  
3747      if( empty( $params['chapter_ID'] ) || empty( $Blog ) )
3748      { // No chapter ID, Exit here
3749          return;
3750      }
3751  
3752      if( $params['chapters_items_mode'] == 'order' )
3753      { // Get all subchapters in this mode to following insertion into posts list below
3754          $sub_chapters = manual_get_chapters( $params['chapter_ID'] );
3755      }
3756  
3757      // Get the posts of current category
3758      $ItemList = new ItemList2( $Blog, $Blog->get_timestamp_min(), $Blog->get_timestamp_max(), $Blog->get_setting('posts_per_page') );
3759      $ItemList->load_from_Request();
3760      $ItemList->set_filters( array(
3761              'cat_array' => array( $params['chapter_ID'] ), // Limit only by selected cat (exclude posts from child categories)
3762              'unit'      => 'all', // Display all items of this category, Don't limit by page
3763          ) );
3764      $ItemList->query();
3765  
3766      // Split items in two arrays to know what items are from main category and what items are from extra category
3767      $items_main = array();
3768      $items_extra = array();
3769      while( $cur_Item = $ItemList->get_item() )
3770      {
3771          if( $cur_Item->main_cat_ID == $params['chapter_ID'] )
3772          { // Item is from main category
3773              $items_main[] = $cur_Item;
3774          }
3775          else
3776          { // Item is from extra catogry
3777              $items_extra[] = $cur_Item;
3778          }
3779      }
3780  
3781  
3782      // ---- Display Items from MAIN category ---- //
3783      $prev_item_order = 0;
3784      foreach( $items_main as $cur_Item )
3785      {
3786          if( $params['chapters_items_mode'] == 'order' )
3787          { // In this mode we display the chapters inside a posts list
3788              foreach( $sub_chapters as $s => $sub_Chapter )
3789              { // Loop through categories to find for current order
3790                  if( ( $sub_Chapter->get( 'order' ) <= $cur_Item->get( 'order' ) && $sub_Chapter->get( 'order' ) > $prev_item_order ) ||
3791                              /* This condition is needed for NULL order: */
3792                              ( $cur_Item->get( 'order' ) == 0 && $sub_Chapter->get( 'order' ) >= $cur_Item->get( 'order' ) ) )
3793                  { // Display chapter
3794                      manual_display_chapter( array_merge( $params, array(
3795                              'Chapter'      => $sub_Chapter,
3796                          ) ), $level );
3797                      // Remove this chapter from array to avoid the duplicates
3798                      unset( $sub_chapters[ $s ] );
3799                  }
3800              }
3801  
3802              // Save current post order for next iteration
3803              $prev_item_order = $cur_Item->get( 'order' );
3804          }
3805  
3806          manual_display_post_row( $cur_Item, $level, array(
3807                  'post_navigation' => 'same_category', // we are always navigating through category in this skin
3808                  'nav_target'      => $params['chapter_ID'], // set the category ID as nav target
3809                  'link_type'       => 'permalink',
3810                  'title_field'     => 'urltitle',
3811              ) );
3812      }
3813  
3814      if( $params['chapters_items_mode'] == 'order' )
3815      {
3816          foreach( $sub_chapters as $s => $sub_Chapter )
3817          { // Loop through rest categories that have order more than last item
3818              manual_display_chapter( array_merge( $params, array(
3819                      'Chapter' => $sub_Chapter,
3820                  ) ), $level );
3821              // Remove this chapter from array to avoid the duplicates
3822              unset( $sub_chapters[ $s ] );
3823          }
3824      }
3825  
3826  
3827      // ---- Display Items from EXTRA category ---- //
3828      foreach( $items_extra as $cur_Item )
3829      {
3830          manual_display_post_row( $cur_Item, $level, array(
3831              'post_navigation' => 'same_category', // we are always navigating through category in this skin
3832              'nav_target'      => $params['chapter_ID'], // set the category ID as nav target
3833              'link_type'       => 'permalink',
3834              'title_field'     => 'urltitle',
3835              'title_before'    => '<i>',
3836              'title_after'     => '</i>',
3837          ) );
3838      }
3839  }
3840  
3841  /**
3842   * Get chapters
3843   *
3844   * @param integer Chapter parent ID
3845   */
3846  function manual_get_chapters( $parent_ID = 0 )
3847  {
3848      global $Blog, $skin_chapters_cache;
3849  
3850      if( !isset( $skin_chapters_cache ) )
3851      { // Get the all chapters for current blog
3852          $ChapterCache = & get_ChapterCache();
3853          $ChapterCache->load_subset( $Blog->ID );
3854  
3855          if( isset( $ChapterCache->subset_cache[ $Blog->ID ] ) )
3856          {
3857              $chapters = $ChapterCache->subset_cache[ $Blog->ID ];
3858  
3859              $skin_chapters_cache = array();
3860              foreach( $chapters as $chapter_ID => $Chapter )
3861              { // Init children
3862                  //pre_dump( $Chapter->ID.' - '.$Chapter->get_name().' : '.$Chapter->get( 'parent_ID' ) );
3863                  if( $Chapter->get( 'parent_ID' ) == 0 )
3864                  {
3865                      $Chapter->children = manual_get_chapter_children( $Chapter->ID );
3866                      $skin_chapters_cache[ $Chapter->ID ] = $Chapter;
3867                  }
3868              }
3869          }
3870      }
3871  
3872      if( $parent_ID > 0 )
3873      { // Get the chapters by parent
3874          $ChapterCache = & get_ChapterCache();
3875          if( $Chapter = & $ChapterCache->get_by_ID( $parent_ID, false ) )
3876          {
3877              return $Chapter->children;
3878          }
3879          else
3880          { // Invalid ID of parent category
3881              return array();
3882          }
3883      }
3884  
3885      return $skin_chapters_cache;
3886  }
3887  
3888  
3889  /**
3890   * Get the children of current chapter recursively
3891   *
3892   * @param integer Parent ID
3893   * @return array Chapter children
3894   */
3895  function manual_get_chapter_children( $parent_ID = 0 )
3896  {
3897      global $blog;
3898  
3899      $ChapterCache = & get_ChapterCache();
3900  
3901      $chapter_children = array();
3902      if( isset( $ChapterCache->subset_cache[ $blog ] ) )
3903      {
3904          $chapters = $ChapterCache->subset_cache[ $blog ];
3905          foreach( $chapters as $Chapter )
3906          {
3907              if( $parent_ID == $Chapter->get( 'parent_ID' ) )
3908              {
3909                  $Chapter->children = manual_get_chapter_children( $Chapter->ID );
3910                  $chapter_children[ $Chapter->ID ] = $Chapter;
3911              }
3912          }
3913      }
3914  
3915      return $chapter_children;
3916  }
3917  
3918  
3919  /**
3920   * Get an array with chapters ID that located in current path
3921   *
3922   * @param integer Chapter ID
3923   * @return array Chapters ID
3924   */
3925  function manual_get_chapter_path( $chapter_ID )
3926  {
3927      global $blog;
3928      $ChapterCache = & get_ChapterCache();
3929      $ChapterCache->load_subset( $blog );
3930  
3931      $chapter_path = array( $chapter_ID );
3932      if( isset( $ChapterCache->subset_cache[ $blog ] ) )
3933      {
3934          $chapters = $ChapterCache->subset_cache[ $blog ];
3935          if( isset( $chapters[ $chapter_ID ] ) )
3936          {
3937              $Chapter = $chapters[ $chapter_ID ];
3938              while( $Chapter->get( 'parent_ID' ) > 0 )
3939              {
3940                  $chapter_path[] = $Chapter->get( 'parent_ID' );
3941                  // Select a parent chapter
3942                  $Chapter = $chapters[ $Chapter->get( 'parent_ID' ) ];
3943              }
3944          }
3945      }
3946  
3947      return $chapter_path;
3948  }
3949  
3950  
3951  /**
3952   * Display chapter row
3953   *
3954   * @param object Chapter
3955   * @param integer Level of the category in the recursive tree
3956   * @param boolean TRUE - if category is opened
3957   */
3958  function manual_display_chapter_row( $Chapter, $level, $is_opened = false )
3959  {
3960      global $line_class, $current_User, $Settings;
3961      global $admin_url;
3962      global $Session;
3963  
3964      $result_fadeout = $Session->get( 'fadeout_array' );
3965  
3966      $line_class = $line_class == 'even' ? 'odd' : 'even';
3967  
3968      $perm_edit = $current_User->check_perm( 'blog_cats', '', false, $Chapter->blog_ID );
3969      $perm_create_item = $current_User->check_perm( 'blog_post_statuses', 'edit', false, $Chapter->blog_ID );
3970  
3971      // Redirect to manual pages after adding/editing chapter
3972      $redirect_page = '&amp;redirect_page=manual';
3973  
3974      $r = '<tr id="cat-'.$Chapter->ID.'" class="'.$line_class.( isset( $result_fadeout ) && in_array( $Chapter->ID, $result_fadeout ) ? ' fadeout-ffff00': '' ).'">';
3975  
3976      // Name
3977      if( $is_opened )
3978      { // Chapter is expanded
3979          $cat_icon = get_icon( 'filters_hide' );
3980          $param_cat = '';
3981          if( $parent_Chapter = & $Chapter->get_parent_Chapter() )
3982          {
3983              $param_cat = '&amp;cat_ID='.$parent_Chapter->ID;
3984          }
3985          $open_url = $admin_url.'?ctrl=items&amp;tab=manual'.$param_cat;
3986      }
3987      else
3988      { // Chapter is collapsed
3989          $cat_icon = get_icon( 'filters_show' );
3990          $open_url = $admin_url.'?ctrl=items&amp;tab=manual&amp;cat_ID='.$Chapter->ID;
3991      }
3992      $r .= '<td class="firstcol">'
3993                      .'<strong style="padding-left: '.($level).'em;">'
3994                          .'<a href="'.$open_url.'">'.$cat_icon.'</a>';
3995      if( $perm_edit )
3996      { // Current user can edit the chapters of the blog
3997          $edit_url = $admin_url.'?ctrl=chapters&amp;blog='.$Chapter->blog_ID.'&amp;cat_ID='.$Chapter->ID.'&amp;action=edit'.$redirect_page;
3998          $r .= '<a href="'.$edit_url.'" title="'.T_('Edit...').'">'.$Chapter->dget('name').'</a>';
3999      }
4000      else
4001      {
4002          $r .= $Chapter->dget('name');
4003      }
4004      $r .= '</strong></td>';
4005  
4006      // URL "slug"
4007      $r .= '<td><a href="'.htmlspecialchars($Chapter->get_permanent_url()).'">'.$Chapter->dget('urlname').'</a></td>';
4008  
4009      // Order
4010      $order_attrs = '';
4011      if( $perm_edit )
4012      { // Add availability to edit an order if current user can edit chapters
4013          $order_attrs = ' id="order-chapter-'.$Chapter->ID.'" title="'.format_to_output( T_('Click to change an order'), 'htmlattr' ).'"';
4014      }
4015      $r .= '<td class="center"'.$order_attrs.'>'.$Chapter->dget('order').'</td>';
4016  
4017      // Actions
4018      $r .= '<td class="lastcol shrinkwrap">';
4019      if( $perm_edit || $perm_create_item )
4020      { // Current user can edit the chapters of the blog or can create item in the blog
4021          if( $perm_edit )
4022          { // Create/Edit chapter, Move to another blog
4023              $r .= action_icon( T_('Edit...'), 'edit', $edit_url );
4024              if( $Settings->get('allow_moving_chapters') )
4025              { // If moving cats between blogs is allowed:
4026                  $r .= action_icon( T_('Move to a different blog...'), 'file_move', $admin_url.'?ctrl=chapters&amp;blog='.$Chapter->blog_ID.'&amp;cat_ID='.$Chapter->ID.'&amp;action=move', T_('Move') );
4027              }
4028              $r .= action_icon( T_('New chapter...'), 'add', $admin_url.'?ctrl=chapters&amp;blog='.$Chapter->blog_ID.'&amp;cat_parent_ID='.$Chapter->ID.'&amp;action=new'.$redirect_page );
4029          }
4030          if( $perm_create_item )
4031          { // Create new item
4032              $redirect_to = '&amp;redirect_to='.urlencode( $admin_url.'?ctrl=items&tab=manual&cat_ID='.$Chapter->ID );
4033              $r .= action_icon( T_('New manual page...'), 'new', $admin_url.'?ctrl=items&action=new&blog='.$Chapter->blog_ID.'&amp;cat='.$Chapter->ID.$redirect_to, NULL, NULL, NULL, array(), array( 'style' => 'width:12px' ) );
4034          }
4035          if( $perm_edit )
4036          { // Delete chapter
4037              $r .= action_icon( T_('Delete chapter...'), 'delete', $admin_url.'?ctrl=chapters&amp;blog='.$Chapter->blog_ID.'&amp;cat_ID='.$Chapter->ID.'&amp;action=delete&amp;'.url_crumb('element').$redirect_page );
4038          }
4039      }
4040      else
4041      {
4042          $r .= '&nbsp;';
4043      }
4044      $r .= '</td>';
4045  
4046      $r .= '</tr>';
4047  
4048      echo $r;
4049  }
4050  
4051  
4052  /**
4053   * Display item row
4054   *
4055   * @param object Item
4056   * @param integer Level of the category in the recursive tree
4057   * @param array Params
4058   */
4059  function manual_display_post_row( $Item, $level, $params = array() )
4060  {
4061      global $line_class, $current_User, $Settings;
4062      global $admin_url;
4063      global $Session;
4064  
4065      $result_fadeout = $Session->get( 'fadeout_array' );
4066  
4067      $params = array_merge( array(
4068              'title_before' => '',
4069              'title_after'  => '',
4070          ), $params );
4071  
4072      $line_class = $line_class == 'even' ? 'odd' : 'even';
4073  
4074      $r = '<tr id="item-'.$Item->ID.'" class="'.$line_class.( isset( $result_fadeout ) && in_array( 'item-'.$Item->ID, $result_fadeout ) ? ' fadeout-ffff00': '' ).'">';
4075  
4076      // Title
4077      $edit_url = $Item->ID;
4078      $item_icon = get_icon( 'post', 'imgtag', array( 'title' => '' ) );
4079      $item_edit_url = $Item->get_edit_url();
4080      $r .= '<td class="firstcol"><strong style="padding-left: '.($level).'em;">';
4081      if( !empty( $item_edit_url ) )
4082      { // If current user can edit this item
4083          $r .= '<a href="'.$Item->get_edit_url().'" title="'.T_('Edit...').'">';
4084      }
4085      else
4086      {
4087          $r .= '';
4088      }
4089      $r .= $params['title_before']
4090              .$item_icon
4091              .$Item->dget('title')
4092              .$params['title_after'];
4093      $r .= !empty( $item_edit_url ) ? '</a>' : '';
4094      $r .= '</strong></td>';
4095  
4096      // URL "slug"
4097      $edit_url = regenerate_url( 'action,cat_ID', 'cat_ID='.$Item->ID.'&amp;action=edit' );
4098      $r .= '<td>'.$Item->get_title( $params );
4099      if( $current_User->check_perm( 'slugs', 'view', false ) )
4100      { // Display icon to view all slugs of this item if current user has permission
4101          $r .= ' '.action_icon( T_('Edit slugs...'), 'edit', $admin_url.'?ctrl=slugs&amp;slug_item_ID='.$Item->ID );
4102      }
4103      $r .= '</td>';
4104  
4105      // Order
4106      $order_attrs = '';
4107      if( $current_User->check_perm( 'item_post!CURSTATUS', 'edit', false, $Item ) )
4108      { // Add availability to edit an order if current user can edit this item
4109          $order_attrs = ' id="order-item-'.$Item->ID.'" title="'.format_to_output( T_('Click to change an order'), 'htmlattr' ).'"';
4110      }
4111      $r .= '<td class="center"'.$order_attrs.'>'.$Item->dget('order').'</td>';
4112  
4113      // Actions
4114      $r .= '<td class="lastcol shrinkwrap">'.item_edit_actions( $Item ).'</td>';
4115  
4116      $r .= '</tr>';
4117  
4118      echo $r;
4119  }
4120  
4121  /**
4122   * End of helper functions block to display Items results.
4123   * New ( not display helper ) functions must be created above items_results function.
4124   */
4125  
4126  ?>

title

Description

title

Description

title

Description

title

title

Body