Source for file _item.class.php
Documentation is available at _item.class.php
* This file implements the Item class.
* This file is part of the evoCore framework - {@link http://evocore.net/}
* See also {@link http://sourceforge.net/projects/evocms/}.
* @copyright (c)2003-2008 by Francois PLANQUE - {@link http://fplanque.net/}
* Parts of this file are copyright (c)2004-2006 by Daniel HAHLER - {@link http://thequod.de/contact}.
* {@internal License choice
* - If you have received this file as part of a package, please find the license.txt file in
* the same folder or the closest folder above for complete license terms.
* - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
* then you must choose one of the following licenses before using the file:
* - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
* - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
* {@internal Open Source relicensing agreement:
* Daniel HAHLER grants Francois PLANQUE the right to license
* Daniel HAHLER's contributions to this file and the b2evolution project
* under any OSI approved OSS license (http://www.opensource.org/licenses/).
* {@internal Below is a list of authors who have contributed to design/coding of this file: }}
* @author blueyed: Daniel HAHLER.
* @author fplanque: Francois PLANQUE.
* @author gorgeb: Bertrand GORGE / EPISTEMA
* @author mbruneau: Marc BRUNEAU / PROGIDISTRI
* @version $Id: _item.class.php,v 1.32.2.8 2009/06/13 18:50:47 tblue246 Exp $
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
* The User who has created the Item (lazy-filled).
* @see Item::get_creator_User()
* @see Item::set_creator_User()
* @deprecated by {@link $creator_User}
* ID of the user that created the item
* The assigned User to the item.
* @see Item::get_assigned_User()
* ID of the user that created the item
* The visibility status of the item.
* 'published', 'deprecated', 'protected', 'private' or 'draft'
* Locale code for the Item content.
* Examples: en-US, zh-CN-utf-8
* Lazy filled, use split_page()
* The list of renderers, imploded by '.'.
* "open", "disabled" or "closed
* Have post processing notifications been handled?
* Which cron task is responsible for handling notifications?
* array of IDs or NULL if we don't know...
* Array of tags (strings)
* Array of Links attached to this item.
* NULL when not initialized.
* Pre-rendered content, cached by format/validated renderers.
* @see Item::get_prerendered_content()
* @param object table Database row
* @param string for derived classes
* @param string datetime field name
* @param string datetime field name
* @param string User ID field name
* @param string User ID field name
function Item( $db_row =
NULL, $dbtable =
'T_items__item', $dbprefix =
'post_', $dbIDname =
'post_ID', $objtype =
'Item',
$datecreated_field =
'datecreated', $datemodified_field =
'datemodified',
$creator_field =
'creator_user_ID', $lasteditor_field =
'lastedit_user_ID' )
1 =>
/* TRANS: Priority name */ T_('1 - Highest'),
2 =>
/* TRANS: Priority name */ T_('2 - High'),
3 =>
/* TRANS: Priority name */ T_('3 - Medium'),
4 =>
/* TRANS: Priority name */ T_('4 - Low'),
5 =>
/* TRANS: Priority name */ T_('5 - Lowest'),
// Call parent constructor:
parent::ItemLight( $db_row, $dbtable, $dbprefix, $dbIDname, $objtype,
$datecreated_field, $datemodified_field,
$creator_field, $lasteditor_field );
if( isset
($current_User) )
{ // use current user as default, if available (which won't be the case during install)
$this->set( 'notifications_status', 'noreq' );
// Set the renderer list to 'default' will trigger all 'opt-out' renderers:
$this->set( 'renderers', array('default') );
$this->set( 'status', 'published' );
$this->set( 'locale', $default_locale );
$this->set( 'priority', 3 );
$this->set( 'ptyp_ID', 1 /* Post */ );
$this->datecreated =
$db_row->post_datecreated; // Needed for history display
$this->creator_user_ID =
$db_row->post_creator_user_ID; // Needed for history display
$this->lastedit_user_ID =
$db_row->post_lastedit_user_ID; // Needed for history display
$this->status =
$db_row->post_status;
$this->content =
$db_row->post_content;
$this->pst_ID =
$db_row->post_pst_ID;
$this->priority =
$db_row->post_priority;
$this->locale =
$db_row->post_locale;
$this->comment_status =
$db_row->post_comment_status; // Comments status
// echo 'renderers=', $db_row->post_renderers;
$this->views =
$db_row->post_views;
* @todo use extended dbchange instead of set_param...
* @todo Normalize to set_assigned_User!?
function assign_to( $user_ID, $dbupdate =
true /* BLOAT!? */ )
// echo 'assigning user #'.$user_ID;
$this->set_param( 'assigned_user_ID', 'number', $user_ID, true );
// fp>> DO NOT set (to null) immediately OR it may KILL the current User object (big problem if it's the Current User)
$this->set_param( 'assigned_user_ID', 'number', NULL, true );
* Template function: display author/creator of item
* @param string String to display before author name
* @param string String to display after author name
* @param string Output format, see {@link format_to_output()}
function author( $params =
array() )
// Make sure we are not missing any param:
* Load data from Request form fields.
* This requires the blog (e.g. {@link $blog_ID} or {@link $main_cat_ID} to be set).
* @param boolean true to force edit date (as long as perms permit)
* @return boolean true if loaded data seems valid.
global $default_locale, $current_User;
if( param( 'post_locale', 'string', NULL ) !==
NULL )
if( param( 'item_typ_ID', 'integer', NULL ) !==
NULL )
if( param( 'post_url', 'string', NULL ) !==
NULL )
// Note: post_url is not part of the simple form, so this message can be a little bit awkward there
if( $this->status ==
'redirected' &&
empty($this->url) )
param_error( 'post_url', T_('If you want to redirect this post, you must specify an URL! (Expert mode)') );
if( ( $force_edit_date ||
param( 'edit_date', 'integer', 0 ) )
&&
$current_User->check_perm( 'edit_timestamp' ) )
{ // We can use user date:
param_date( 'item_issue_date', T_('Please enter a valid issue date.'), $force_edit_date /* required */ );
{ // only set it, if a date was given:
if( param( 'post_excerpt', 'string', NULL ) !==
NULL ) {
if( param( 'post_urltitle', 'string', NULL ) !==
NULL ) {
if( param( 'item_tags', 'string', NULL ) !==
NULL ) {
// pre_dump( $this->tags );
if( param( 'item_st_ID', 'integer', NULL ) !==
NULL ) {
if( param( 'item_assigned_user_ID', 'integer', NULL ) !==
NULL ) {
if( param( 'item_priority', 'integer', NULL ) !==
NULL ) {
if( param_date( 'item_deadline', T_('Please enter a valid deadline.'), false, NULL ) !==
NULL ) {
// Allow comments for this item (only if set to "post_by_post" for the Blog):
if( $this->Blog->allowcomments ==
'post_by_post' )
if( param( 'post_comment_status', 'string', 'open' ) !==
NULL )
{ // 'open' or 'closed' or ...
if( param( 'renderers_displayed', 'integer', 0 ) )
{ // use "renderers" value only if it has been displayed (may be empty)
$Plugins_admin =
& get_Cache('Plugins_admin');
$renderers =
$Plugins_admin->validate_renderer_list( param( 'renderers', 'array', array() ) );
$this->set( 'renderers', $renderers );
if( param( 'content', 'html', NULL ) !==
NULL )
param( 'post_title', 'html', NULL );
// Do some optional filtering on the content
// Typically stuff that will help the content to validate
// Useful for code display.
// Will probably be used for validation also.
$Plugins_admin =
& get_Cache('Plugins_admin');
$Plugins_admin->filter_contents( $GLOBALS['post_title'] /* by ref */, $GLOBALS['content'] /* by ref */, $renderers );
// Format raw HTML input to cleaned up and validated HTML:
* Template function: display anchor for permalinks to refer to.
// In case you have old cafelog permalinks, uncomment the following line:
// return preg_replace( '/[^a-zA-Z0-9_\.-]/', '_', $this->title );
return 'item_'.
$this->ID;
* Template function: display assignee of item
* @param string Output format, see {@link format_to_output()}
function assigned_to( $before =
'', $after =
'', $format =
'htmlbody' )
* Get list of assigned user options
* @uses UserCache::get_blog_member_option_list()
* @return string HTML select options list
true, ($this->ID !=
0) /* if this Item is already serialized we'll load the default anyway */ );
* Check if user can see comments on this post, which he cannot if they
* are disabled for the Item or never allowed for the blog.
||
( $this->get_Blog() &&
$this->Blog->allowcomments ==
'never' ) )
{ // Comments are disabled on this post
return true; // OK, user can see comments
* Template function: Check if user can leave comment on this post or display error
* @param string|NULLstring to display before any error message; NULL to not display anything, but just return boolean
* @param string string to display after any error message
* @param string error message for non published posts, '#' for default
* @param string error message for closed comments posts, '#' for default
* @return boolean true if user can post, false if s/he cannot
function can_comment( $before_error =
'<p><em>', $after_error =
'</em></p>', $non_published_msg =
'#', $closed_msg =
'#' )
$display =
( ! is_null($before_error) );
// Ask Plugins (it can say NULL and would get skipped in Plugin::trigger_event_first_return()):
// - A plugin might want to restrict comments on posts older than 20 days.
// - A plugin might want to allow comments always for certain users (admin).
if( $event_return =
$Plugins->trigger_event_first_return( 'ItemCanComment', array( 'Item' =>
$this ) ) )
$plugin_return_value =
$event_return['plugin_return'];
if( $plugin_return_value ===
true )
return true; // OK, user can comment!
if( $display &&
is_string($plugin_return_value) )
echo
$plugin_return_value;
if( $this->Blog->allowcomments ==
'never')
if( $this->Blog->allowcomments ==
'always')
{ // Comments are disabled on this post
{ // Comments are closed on this post
$closed_msg =
T_( 'Comments are closed for this post.' );
if( ($this->status ==
'draft') ||
($this->status ==
'deprecated' ) ||
($this->status ==
'redirected' ) )
{ // Post is not published
if( $non_published_msg ==
'#' )
$non_published_msg =
T_( 'This post is not published. You cannot leave comments.' );
return true; // OK, user can comment!
* Template function: Check if user can can rate this post
* @return boolean true if user can post, false if s/he cannot
if( $this->Blog->get_setting('allow_rating') ==
'never' )
return true; // OK, user can rate!
* Get the prerendered content. If it has not been generated yet, it will.
* @todo dh> Currently this makes up one query per displayed item. Probably the cache should get pre-fetched by ItemList2?
* fp> DEFINITELY!!! Preloading all pre-rendered contents for the current Itemlistpage is paramount!
* @todo dh> In general, $content_prerendered gets only queried once per item, so it seems like a memory waste to cache the query result..!
* fp> I don't know if this is supposed to be related but that doesn't change anything to the previous todo.
* NOTE: This calls {@link Item::dbupdate()}, if renderers get changed (from Plugin hook).
* @param string Format, see {@link format_to_output()}.
* Only "htmlbody", "entityencoded", "xml" and "text" get cached.
$cache_key =
$format.
'/'.
implode('.', $post_renderers);
$use_cache =
$this->ID &&
in_array( $format, array( 'htmlbody', 'entityencoded', 'xml', 'text' ) );
{ // the format/item can be cached:
SELECT itpr_content_prerendered
FROM T_items__prerendering
WHERE itpr_itm_ID = ".
$this->ID.
"
AND itpr_format = '".
$format.
"'
AND itpr_renderers = '".
implode('.', $post_renderers).
"'", 0, 0, 'Check prerendered item content' );
if( $cache !==
NULL ) // may be empty string
{ // Retrieved from cache:
// echo ' retrieved from prerendered cache';
$cache_key =
$format.
'/'.
implode('.', $post_renderers);
// Save new renderers with item:
// Call RENDERER plugins:
// pre_dump( $this->content );
$Plugins->render( $this->content_prerendered[$cache_key] /* by ref */, $post_renderers, $format, array( 'Item' =>
$this ), 'Render' );
// pre_dump( $this->content_prerendered[$cache_key] );
$Debuglog->add( 'Generated pre-rendered content ['.
$cache_key.
'] for item #'.
$this->ID, 'items' );
{ // save into DB (using REPLACE INTO because it may have been pre-rendered by another thread since the SELECT above)
REPLACE INTO T_items__prerendering (itpr_itm_ID, itpr_format, itpr_renderers, itpr_content_prerendered)
VALUES ( ".
$this->ID.
", '".
$format.
"', ".
$DB->quote(implode('.', $post_renderers)).
', '.
$DB->quote($this->content_prerendered[$cache_key]).
' )', 'Cache prerendered item content' );
$Debuglog->add( 'Fetched pre-rendered content ['.
$cache_key.
'] for item #'.
$this->ID, 'items' );
* Set the pre-rendered content.
* This is meant to get called by ItemList2, which would do a single query for all
* @param string Pre-rendered content
* @param string Cache-Key ($format.'/'.$renderers). See {@link Item::get_prerendered_content()} for the appropriate query skeleton.
* Trigger {@link Plugin::ItemApplyAsRenderer()} event and adjust renderers according
* @return boolean True if renderers got changed.
foreach( $Plugins->get_list_by_event('ItemApplyAsRenderer') as $Plugin )
if( empty($Plugin->code) )
$plugin_r =
$Plugin->ItemApplyAsRenderer( $tmp_params =
array('Item' =>
& $this) );
* Make sure, the pages have been obtained (and split up_ from prerendered cache.
* @param string Format, used to retrieve the matching cache; see {@link format_to_output()}
// echo ' Pages:'.$this->pages;
* Get a specific page to display (from the prerendered cache)
* @param integer Page number
* @param string Format, used to retrieve the matching cache; see {@link format_to_output()}
// Make sure, the pages are split up:
if( $page >
$this->pages )
* This is like a teaser with no HTML and a cropping.
* @todo fp> allow use to submit his own excerpt in expert editing mode
// Get teaser for page 1:
// fp> Note: I'm not sure about using 'text' here, but there should definitely be no rendering here.
// Ger rid of all new lines:
$output =
trim( str_replace( array( "\r", "\n", "\t" ), array( ' ', ' ', ' ' ), $output ) );
if( strlen( $output ) >
$crop_at )
$output =
substr( $output, 0, $crop_at ).
'...';
* Display content teaser of item (will stop at "<!-- more -->"
// Make sure we are not missing any param:
$r =
$this->get_content_teaser( $params['disppage'], $params['stripteaser'], $params['format'] );
* Template function: get content teaser of item (will stop at "<!-- more -->"
* @param mixed page number to display specific page, # for url parameter
* @param boolean # if you don't want to repeat teaser after more link was pressed and <-- noteaser --> has been found
* @param string filename to use to display more
// Get requested content page:
{ // We want to display the page requested by the user:
$content_page =
$this->get_content_page( $disppage, $format ); // cannot include format_to_output() because of the magic below.. eg '<!--more-->' will get stripped in "xml"
// pre_dump($content_page);
$content_parts =
explode( '<!--more-->', $content_page );
// echo ' Parts:'.count($content_parts);
if( count($content_parts) >
1 )
{ // This is an extended post (has a more section):
if( $stripteaser ===
'#' )
// If we're in "more" mode and we want to strip the teaser, we'll strip:
$stripteaser =
( $more &&
preg_match('/<!--noteaser-->/', $content_page ) );
$output =
$content_parts[0];
// Trigger Display plugins FOR THE STUFF THAT WOULD NOT BE PRERENDERED:
'dispmore' =>
($more !=
0),
// ---------------------- POST CONTENT INCLUDED HERE ----------------------
'image_size' =>
'fit-400x320',
// Note: You can customize the default item feedback by copying the generic
// /skins/_item_feedback.inc.php file into the current skin folder.
// -------------------------- END OF POST CONTENT -------------------------
* Display content teaser of item (will stop at "<!-- more -->"
// Make sure we are not missing any param:
* Template function: get content extension of item (part after "<!-- more -->")
* @param mixed page number to display specific page, # for url parameter
* @param string filename to use to display more
global $Plugins, $more, $preview;
if( ! $more &&
! $force_more )
// Get requested content page:
{ // We want to display the page requested by the user:
$content_page =
$this->get_content_page( $disppage, $format ); // cannot include format_to_output() because of the magic below.. eg '<!--more-->' will get stripped in "xml"
// pre_dump($content_page);
$content_parts =
explode( '<!--more-->', $content_page );
// echo ' Parts:'.count($content_parts);
if( count($content_parts) <
2 )
{ // This is NOT an extended post
// Output everything after <!-- more -->
$output =
implode('', $content_parts);
// Trigger Display plugins FOR THE STUFF THAT WOULD NOT BE PRERENDERED:
* @todo merge with inc_viewcount
// Make sure we are not missing any param:
'allow_multiple_counts_per_page' =>
false,
* Check if we want to increment view count, see {@link Hit::is_new_view()}
if( ! $Hit->is_new_view() )
if( ! $params['allow_multiple_counts_per_page'] )
{ // Check that we don't increase multiple viewcounts on the same page
// This make the assumption that the first post in a list is "viewed" and the other are not (necesarily)
global $view_counts_on_this_page;
if( $view_counts_on_this_page >=
1 )
{ // we already had a count on this page
// echo 'ALREADY HAD A COUNT';
$view_counts_on_this_page++
;
// Increment view counter (only if current User is not the item's author)
return $this->inc_viewcount(); // won't increment if current_User == Author
// Make sure we are not missing any param:
'before' =>
'<p class="bMore">',
'link_text' =>
'#', // text to display as the more link
'anchor_text' =>
'#', // text to display as the more anchor (once the more link has been clicked)
'disppage' =>
'#', // page number to display specific page, # for url parameter
// Get requested content page:
if( $params['disppage'] ===
'#' )
{ // We want to display the page requested by the user:
$params['disppage'] =
$page;
$content_page =
$this->get_content_page( $params['disppage'], $params['format'] ); // cannot include format_to_output() because of the magic below.. eg '<!--more-->' will get stripped in "xml"
// pre_dump($content_page);
$content_parts =
explode( '<!--more-->', $content_page );
// echo ' Parts:'.count($content_parts);
if( count($content_parts) <
2 )
{ // This is NOT an extended post:
{ // We're NOT in "more" mode:
if( $params['link_text'] ==
'#' )
{ // TRANS: this is the default text for the extended post "more" link
$params['link_text'] =
T_('Read more').
' »';
.
$params['link_text'].
'</a>'
.
$params['after'], $params['format'] );
elseif( ! preg_match('/<!--noteaser-->/', $content_page ) )
{ // We are in mode mode and we're not hiding the teaser:
// (if we're higin the teaser we display this as a normal page ie: no anchor)
if( $params['anchor_text'] ==
'#' )
{ // TRANS: this is the default text displayed once the more link has been activated
$params['anchor_text'] =
'<p class="bMore">'.
T_('Follow up:').
'</p>';
.
$params['anchor_text'], $params['format'] );
* Template function: display deadline date (datetime) of Item
* @param string date/time format: leave empty to use locale default date format
* @param boolean true if you want GMT
* Template function: display deadline time (datetime) of Item
* @param string date/time format: leave empty to use locale default time format
* @param boolean true if you want GMT
* Get reference to array of Links
// Make sure links are loaded:
* Template function: display number of links attached to this Item
// Make sure links are loaded:
* Load links if they were not loaded yet.
{ // Links have not been loaded yet:
$this->Links =
& $LinkCache->get_by_item_ID( $this->ID );
* Load from DB if necessary
if( !isset
( $this->tags ) )
$this->tags =
$DB->get_col(
FROM T_items__itemtag INNER JOIN T_items__tag ON itag_tag_ID = tag_ID
WHERE itag_itm_ID = '.
$this->ID.
'
ORDER BY tag_name', 0, 'Get tags for Item' );
// pre_dump( $this->tags );
* Split tags by comma or semicolon
* @param string The tags, separated by comma or semicolon
$this->tags =
preg_split( '/\s*[;,]+\s*/', $tags, -
1, PREG_SPLIT_NO_EMPTY );
{ // fp> TODO: instead of those "when used" ifs, it would make more sense to redefine mb_strtolower beforehand if it doesn"t exist (it would then just be a fallback to the strtolower + a Debuglog->add() )
array_walk( $this->tags, create_function( '& $tag', '$tag = mb_strtolower( $tag, mb_detect_encoding( $tag.\'a\', \'UTF-8, ISO-8859-1, ISO-8859-15\', true ) );' ) );
{ // Tblue> Note: This doesn't work with ISO-8859-1 umlauts like 'Ä' and won't work with UTF-8 either.
// pre_dump( $this->tags );
* Template function: Provide link to message form for this Item's author.
* @param string url of the message form
* @param string to display before link
* @param string to display after link
* @param string link text
* @param string link title
* @param string class name
* @return boolean true, if a link was displayed; false if there's no email address for the Item's author.
// Make sure we are not missing any param:
'form_url' =>
'#current_blog#',
{ // We have no email for this Author :(
if( $params['form_url'] ==
'#current_blog#' )
$params['form_url'] =
$Blog->get('msgformurl');
if( $params['title'] ==
'#' ) $params['title'] =
T_('Send email to post author');
if( $params['text'] ==
'#' ) $params['text'] =
get_icon( 'email', 'imgtag', array( 'class' =>
'middle', 'title' =>
$params['title'] ) );
echo
'<a href="'.
$params['form_url'].
'" title="'.
$params['title'].
'"';
if( !empty( $params['class'] ) ) echo
' class="'.
$params['class'].
'"';
echo
' rel="nofollow">'.
$params['text'].
'</a>';
* Template function: Provide link to message form for this Item's assigned User.
* @param string url of the message form
* @param string to display before link
* @param string to display after link
* @param string link text
* @param string link title
* @param string class name
* @return boolean true, if a link was displayed; false if there's no email address for the assigned User.
function msgform_link_assigned( $form_url, $before =
' ', $after =
' ', $text =
'#', $title =
'#', $class =
'' )
{ // We have no email for this Author :(
if( $title ==
'#' ) $title =
T_('Send email to assigned user');
if( $text ==
'#' ) $text =
get_icon( 'email', 'imgtag', array( 'class' =>
'middle', 'title' =>
$title ) );
echo
'<a href="'.
$form_url.
'" title="'.
$title.
'"';
if( !empty( $class ) ) echo
' class="'.
$class.
'"';
echo
' rel="nofollow">'.
$text.
'</a>';
function page_links( $before =
'#', $after =
'#', $separator =
' ', $single =
'', $current_page =
'#', $pagelink =
'%d', $url =
'' )
// Make sure, the pages are split up:
if( $before ==
'#' ) $before =
'<p>'.
T_('Pages:').
' ';
if( $after ==
'#' ) $after =
'</p>';
if( $current_page ==
'#' )
for( $i =
1; $i <=
$this->pages; $i++
)
if( $i !=
$current_page )
$page_links[] =
'<a href="'.
$url.
'">'.
$text.
'</a>';
$page_links[] =
'<a href="'.
url_add_param( $url, 'page='.
$i ).
'">'.
$text.
'</a>';
echo
implode( $separator, $page_links );
* Display the images linked to the current Item
* @param string Output format, see {@link format_to_output()}
function images( $params =
array(), $format =
'htmlbody' )
* Get block of images linked to the current Item
* @param string Output format, see {@link format_to_output()}
function get_images( $params =
array(), $format =
'htmlbody' )
'before_image' =>
'<div class="image_block">',
'before_image_legend' =>
'<div class="image_legend">',
'after_image_legend' =>
'</div>',
'after_image' =>
'</div>',
'image_size' =>
'fit-720x500'
$SQL->SELECT( 'file_ID, file_title, file_root_type, file_root_ID, file_path, file_alt, file_desc' );
$SQL->FROM( 'T_links INNER JOIN T_files ON link_file_ID = file_ID' );
$SQL->WHERE( 'link_itm_ID = '.
$this->ID );
$SQL->ORDER_BY( 'link_ID' );
$FileList->sql =
$SQL->get();
$FileList->query( false, false, false );
while( $File =
& $FileList->get_next() )
if( ! $File->is_image() )
{ // Skip anything that is not an image
// fp> TODO: maybe this property should be stored in link_ltype_ID
// Generate the IMG tag with all the alt, title and desc if available
$r .=
$File->get_tag( $params['before_image'], $params['before_image_legend'], $params['after_image_legend'], $params['after_image'], $params['image_size'] );
$r =
$params['before'].
$r.
$params['after'];
* Template function: Displays link to the feed for comments on this item
* @param string Type of feedback to link to (rss2/atom)
* @param string String to display before the link (if comments are to be displayed)
* @param string String to display after the link (if comments are to be displayed)
* @param string Link title
$title =
get_icon( 'feed' ).
' '.
T_('Comment feed for this post');
* Get URL to display the post comments in an XML feed.
return url_add_param( $this->Blog->get_tempskin_url( $skin_folder_name ), 'disp=comments&p='.
$this->ID);
* Template function: Displays link to feedback page (under some conditions)
// dh> TODO: Add plugin hook, where a Pingback plugin could hook and provide "pingbacks"
switch( $params['type'] )
if( $params['link_title'] ==
'#' ) $params['link_title'] =
T_('Display feedback / Leave a comment');
if( $params['link_text_zero'] ==
'#' ) $params['link_text_zero'] =
T_('Send feedback').
' »';
if( $params['link_text_one'] ==
'#' ) $params['link_text_one'] =
T_('1 feedback').
' »';
if( $params['link_text_more'] ==
'#' ) $params['link_text_more'] =
T_('%d feedbacks').
' »';
if( $params['link_title'] ==
'#' ) $params['link_title'] =
T_('Display comments / Leave a comment');
if( $params['link_text_zero'] ==
'#' )
if( $this->can_comment( NULL ) ) // NULL, because we do not want to display errors here!
$params['link_text_zero'] =
T_('Leave a comment').
' »';
$params['link_text_zero'] =
'';
if( $params['link_text_one'] ==
'#' ) $params['link_text_one'] =
T_('1 comment').
' »';
if( $params['link_text_more'] ==
'#' ) $params['link_text_more'] =
T_('%d comments').
' »';
if( ! $this->Blog->get( 'allowtrackbacks' ) )
{ // Trackbacks not allowed on this blog:
if( $params['link_title'] ==
'#' ) $params['link_title'] =
T_('Display trackbacks / Get trackback address for this post');
if( $params['link_text_zero'] ==
'#' ) $params['link_text_zero'] =
T_('Send a trackback').
' »';
if( $params['link_text_one'] ==
'#' ) $params['link_text_one'] =
T_('1 trackback').
' »';
if( $params['link_text_more'] ==
'#' ) $params['link_text_more'] =
T_('%d trackbacks').
' »';
// Obsolete, but left for skin compatibility
if( ! $this->Blog->get( 'allowtrackbacks' ) )
{ // Trackbacks not allowed on this blog:
// We'll consider pingbacks to follow the same restriction
if( $params['link_title'] ==
'#' ) $params['link_title'] =
T_('Display pingbacks');
if( $params['link_text_zero'] ==
'#' ) $params['link_text_zero'] =
T_('No pingback yet').
' »';
if( $params['link_text_one'] ==
'#' ) $params['link_text_one'] =
T_('1 pingback').
' »';
if( $params['link_text_more'] ==
'#' ) $params['link_text_more'] =
T_('%d pingbacks').
' »';
debug_die( "Unknown feedback type [{$params['type']}]" );
$link_text =
$this->get_feedback_title( $params['type'], $params['link_text_zero'], $params['link_text_one'], $params['link_text_more'], $params['status'] );
{ // No link, no display...
if( $params['url'] ==
'#' )
{ // We want a link to single post:
echo
$params['link_before'];
if( !empty( $params['url'] ) )
echo
'<a href="'.
$params['url'];
echo
'#'.
$params['type'].
'" '; // Position on feedback
echo
'title="'.
$params['link_title'].
'"';
if( $params['use_popup'] )
{ // Special URL if we can open a popup (i-e if JS is enabled):
$popup_url =
url_add_param( $params['url'], 'disp=feedback-popup' );
echo
' onclick="return pop_up_window( \''.
$popup_url.
'\', \'evo_comments\' )"';
echo
$params['link_after'];
* Get text depending on number of comments
* @param string Type of feedback to link to (feedbacks (all)/comments/trackbacks/pingbacks)
* @param string Link text to display when there are 0 comments
* @param string Link text to display when there is 1 comment
* @param string Link text to display when there are >1 comments (include %d for # of comments)
* @param string Status of feedbacks to count
function get_feedback_title( $type =
'feedbacks', $zero =
'#', $one =
'#', $more =
'#', $status =
'published' )
// dh> TODO: Add plugin hook, where a Pingback plugin could hook and provide "pingbacks"
if( $zero ==
'#' ) $zero =
'';
if( $one ==
'#' ) $one =
T_('1 feedback');
if( $more ==
'#' ) $more =
T_('%d feedbacks');
if( $zero ==
'#' ) $zero =
'';
if( $one ==
'#' ) $one =
T_('1 comment');
if( $more ==
'#' ) $more =
T_('%d comments');
if( $zero ==
'#' ) $zero =
'';
if( $one ==
'#' ) $one =
T_('1 trackback');
if( $more ==
'#' ) $more =
T_('%d trackbacks');
// Obsolete, but left for skin compatibility
if( $zero ==
'#' ) $zero =
'';
if( $one ==
'#' ) $one =
T_('1 pingback');
if( $more ==
'#' ) $more =
T_('%d pingbacks');
debug_die( "Unknown feedback type [$type]" );
* Template function: Displays feeback moderation info
* @param string Type of feedback to link to (feedbacks (all)/comments/trackbacks/pingbacks)
* @param string String to display before the link (if comments are to be displayed)
* @param string String to display after the link (if comments are to be displayed)
* @param string Link text to display when there are 0 comments
* @param string Link text to display when there is 1 comment
* @param string Link text to display when there are >1 comments (include %d for # of comments)
* @param boolean true to hide if no feedback
$zero =
'', $one =
'#', $more =
'#', $edit_comments_link =
'#', $params =
array() )
if( isset
($current_User) &&
$current_User->check_perm( 'blog_comments', 'any', false, $this->blog_ID ) )
{ // We jave permission to edit comments:
if( $edit_comments_link ==
'#' )
$edit_comments_link =
'<a href="'.
$admin_url.
'?ctrl=items&blog='.
$this->blog_ID.
'&p='.
$this->ID.
'#comments" title="'.
T_('Moderate these feedbacks').
'">'.
get_icon( 'edit' ).
' '.
T_('Moderate...').
'</a>';
{ // User has no right to edit comments:
$edit_comments_link =
'';
// Inject Edit/moderate link as relevant:
$zero =
str_replace( '%s', $edit_comments_link, $zero );
$more =
str_replace( '%s', $edit_comments_link, $more );
* Template tag: display footer for the current Item.
* @return boolean true if something has been displayed
// Make sure we are not missing any param:
'mode' =>
'#', // Will detect 'single' from $disp automatically
'block_start' =>
'<div class="item_footer">',
if( $params['mode'] ==
'#' )
// pre_dump( $params['mode'] );
switch( $params['mode'] )
$text =
$this->Blog->get_setting( 'xml_item_footer_text' );
$text =
$this->Blog->get_setting( 'single_item_footer_text' );
echo
format_to_output( $params['block_start'].
$text.
$params['block_end'], $params['format'] );
* Gets button for deleting the Item if user has proper rights
* @param string to display before link
* @param string to display after link
* @param string link text
* @param string link title
* @param string class name
* @param boolean true to make this a button instead of a link
* @param string page url for the delete action
function get_delete_link( $before =
' ', $after =
' ', $text =
'#', $title =
'#', $class =
'', $button =
false, $actionurl =
'#' )
global $current_User, $admin_url;
if( ! $current_User->check_perm( 'blog_del_post', 'any', false, $this->blog_ID ) )
{ // User has right to delete this post
$text =
get_icon( 'delete', 'imgtag' ).
' '.
T_('Delete!');
if( $title ==
'#' ) $title =
T_('Delete this post');
$actionurl =
$admin_url.
'?ctrl=items&action=delete&post_ID=';
$url =
$actionurl.
$this->ID;
$r .=
'<input type="button"';
$r .=
' value="'.
$text.
'" title="'.
$title.
'" onclick="if ( confirm(\'';
$r .=
TS_('You are about to delete this post!\\nThis cannot be undone!');
$r .=
'\') ) { document.location.href=\''.
$url.
'\' }"';
if( !empty( $class ) ) $r .=
' class="'.
$class.
'"';
$r .=
'<a href="'.
$url.
'" title="'.
$title.
'" onclick="return confirm(\'';
$r .=
TS_('You are about to delete this post!\\nThis cannot be undone!');
if( !empty( $class ) ) $r .=
' class="'.
$class.
'"';
* Displays button for deleting the Item if user has proper rights
* @param string to display before link
* @param string to display after link
* @param string link text
* @param string link title
* @param string class name
* @param boolean true to make this a button instead of a link
* @param string page url for the delete action
function delete_link( $before =
' ', $after =
' ', $text =
'#', $title =
'#', $class =
'', $button =
false, $actionurl =
'#' )
echo
$this->get_delete_link( $before, $after, $text, $title, $class, $button, $actionurl );
* Provide link to edit a post if user has edit rights
* - 'before': to display before link
* - 'after': to display after link
* - 'class': CSS class name
* - 'save_context': redirect to current URL?
global $current_User, $admin_url;
if( ! $current_User->check_perm( 'item_post!'.
$this->status, 'edit', false, $this ) )
{ // User has no right to edit this post
// Make sure we are not missing any param:
if( $params['text'] ==
'#' ) $params['text'] =
get_icon( 'edit' ).
' '.
T_('Edit...');
if( $params['title'] ==
'#' ) $params['title'] =
T_('Edit this post...');
$actionurl =
$admin_url.
'?ctrl=items&action=edit&p='.
$this->ID;
if( $params['save_context'] )
$r .=
'<a href="'.
$actionurl;
$r .=
'" title="'.
$params['title'].
'"';
if( !empty( $params['class'] ) ) $r .=
' class="'.
$params['class'].
'"';
$r .=
'>'.
$params['text'].
'</a>';
* @see Item::get_edit_link()
* Provide link to publish a post if user has edit rights
* Note: publishing date will be updated
* @param string to display before link
* @param string to display after link
* @param string link text
* @param string link title
* @param string class name
* @param string glue between url params
function get_publish_link( $before =
' ', $after =
' ', $text =
'#', $title =
'#', $class =
'', $glue =
'&', $save_context =
true )
global $current_User, $admin_url;
if( ($this->status ==
'published') // Already published!
||
! ($current_User->check_perm( 'item_post!published', 'edit', false, $this ))
||
! ($current_User->check_perm( 'edit_timestamp' ) ) )
{ // User has no right to publish this post now:
if( $text ==
'#' ) $text =
get_icon( 'publish', 'imgtag' ).
' '.
T_('Publish NOW!');
if( $title ==
'#' ) $title =
T_('Publish now using current date and time.');
$r .=
'<a href="'.
$admin_url.
'?ctrl=items'.
$glue.
'action=publish'.
$glue.
'post_ID='.
$this->ID;
$r .=
'" title="'.
$title.
'"';
if( !empty( $class ) ) $r .=
' class="'.
$class.
'"';
function publish_link( $before =
' ', $after =
' ', $text =
'#', $title =
'#', $class =
'', $glue =
'&', $save_context =
true )
echo
$this->get_publish_link( $before, $after, $text, $title, $class, $glue, $save_context );
* Provide link to deprecate a post if user has edit rights
* @param string to display before link
* @param string to display after link
* @param string link text
* @param string link title
* @param string class name
* @param string glue between url params
function get_deprecate_link( $before =
' ', $after =
' ', $text =
'#', $title =
'#', $class =
'', $glue =
'&' )
global $current_User, $admin_url;
if( ($this->status ==
'deprecated') // Already deprecateded!
||
! ($current_User->check_perm( 'item_post!deprecated', 'edit', false, $this )) )
{ // User has no right to deprecated this post:
if( $text ==
'#' ) $text =
get_icon( 'deprecate', 'imgtag' ).
' '.
T_('Deprecate!');
if( $title ==
'#' ) $title =
T_('Deprecate this post!');
$r .=
'<a href="'.
$admin_url.
'?ctrl=items'.
$glue.
'action=deprecate'.
$glue.
'post_ID='.
$this->ID;
$r .=
'" title="'.
$title.
'"';
if( !empty( $class ) ) $r .=
' class="'.
$class.
'"';
* Display link to deprecate a post if user has edit rights
* @param string to display before link
* @param string to display after link
* @param string link text
* @param string link title
* @param string class name
* @param string glue between url params
function deprecate_link( $before =
' ', $after =
' ', $text =
'#', $title =
'#', $class =
'', $glue =
'&' )
* Template function: display priority of item
function priority( $before =
'', $after =
'' )
* Template function: display list of priority options
$priority = isset
($field_value) ?
$field_value :
$this->priority;
$r =
'<option value="">'.
/* TRANS: "None" select option */T_('No priority').
'</option>';
$r .=
'<option value="'.
$i.
'"';
$r .=
' selected="selected"';
$r .=
'>'.
$name.
'</option>';
* Template function: display checkable list of renderers
* @param array|NULLIf given, assume these renderers to be checked.
global $Plugins, $inc_path, $admin_url;
$Plugins->restart(); // make sure iterator is at start position
$atLeastOneRenderer =
false;
// pre_dump( $item_renderers );
echo
'<input type="hidden" name="renderers_displayed" value="1" />';
foreach( $Plugins->get_list_by_events( array('RenderItemAsHtml', 'RenderItemAsXml', 'RenderItemAsText') ) as $loop_RendererPlugin )
{ // Go through whole list of renders
// echo ' ',$loop_RendererPlugin->code;
if( empty($loop_RendererPlugin->code) )
if( $loop_RendererPlugin->apply_rendering ==
'stealth'
||
$loop_RendererPlugin->apply_rendering ==
'never' )
{ // This is not an option.
$atLeastOneRenderer =
true;
// echo $loop_RendererPlugin->apply_rendering;
echo
'<input type="checkbox" class="checkbox" name="renderers[]" value="';
echo
$loop_RendererPlugin->code;
echo
$loop_RendererPlugin->code;
switch( $loop_RendererPlugin->apply_rendering )
echo
' checked="checked"';
echo
' disabled="disabled"';
if( in_array( $loop_RendererPlugin->code, $item_renderers ) // Option is activated
||
in_array( 'default', $item_renderers ) ) // OR we're asking for default renderer set
echo
' checked="checked"';
if( in_array( $loop_RendererPlugin->code, $item_renderers ) ) // Option is activated
echo
' checked="checked"';
if( in_array( $loop_RendererPlugin->code, $item_renderers ) ) // Option is activated
echo
' checked="checked"';
echo
' disabled="disabled"';
.
' <label for="renderer_';
echo
$loop_RendererPlugin->code;
// fp> TODO: the first thing we want here is a TINY javascript popup with the LONG desc. The links to readme and external help should be inside of the tiny popup.
// fp> a javascript DHTML onhover help would be evenb better than the JS popup
// internal README.html link:
echo
' '.
$loop_RendererPlugin->get_help_link('$readme');
echo
' '.
$loop_RendererPlugin->get_help_link('$help_url');
if( !$atLeastOneRenderer )
global $admin_url, $mode;
echo
'<a title="'.
T_('Configure plugins').
'" href="'.
$admin_url.
'?ctrl=plugins"'.
'>'.
T_('No renderer plugins are installed.').
'</a>';
* Template function: display status of item
* @param string Output format, see {@link format_to_output()}
function status( $params =
array() )
// Make sure we are not missing any param:
if( $params['format'] ==
'raw' )
* Template function: display extra status of item
* @param string Output format, see {@link format_to_output()}
function extra_status( $before =
'', $after =
'', $format =
'htmlbody' )
$this->disp( $this->get('t_extra_status'), 'raw' );
elseif( $extra_status =
$this->get('t_extra_status') )
* @param string Output format, see {@link format_to_output()}
function tags( $params =
array() )
'before' =>
'<div>'.
T_('Tags').
': ',
if( $links =
$params['links'] )
echo
$params['separator'];
echo
'<a href="'.
$this->Blog->gen_tag_url( $tag ).
'">';
* Template function: Displays trackback autodiscovery information
* TODO: build into headers
if( ! $this->Blog->get( 'allowtrackbacks' ) )
{ // Trackbacks not allowed on this blog:
echo
'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" '.
"\n";
echo
' xmlns:dc="http://purl.org/dc/elements/1.1/"'.
"\n";
echo
' xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">'.
"\n";
echo
'<rdf:Description'.
"\n";
'before' =>
' dc:title="',
echo
' trackback:ping="';
* Template function: displays url to use to trackback this item
* Template function: get url to use to trackback this item
// fp> TODO: get a clean (per blog) setting for this
// return $htsrv_url.'trackback.php/'.$this->ID;
return $htsrv_url.
'trackback.php?tb_id='.
$this->ID;
* Template function: Display link to item related url.
* By default the link is displayed as a link.
* Optionally some smart stuff may happen.
if( empty( $this->url ) )
// Make sure we are not missing any param:
'text_template' =>
'$url$', // If evaluates to empty, nothing will be displayed (except player if podcast)
'url_template' =>
'$url$',
'podcast' =>
'#', // handle as podcast. # means depending on post type
'before_podplayer' =>
'<div class="podplayer">',
'after_podplayer' =>
'</div>',
if( $params['podcast'] ==
'#' )
{ // Check if this post is a podcast
$params['podcast'] =
( $this->ptyp_ID ==
2000 );
if( $params['podcast'] &&
$params['format'] ==
'htmlbody' )
{ // We want podcast display:
echo
$params['before_podplayer'];
echo
'<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="200" height="20" id="dewplayer" align="middle"><param name="wmode" value="transparent"><param name="allowScriptAccess" value="sameDomain" /><param name="movie" value="'.
$rsc_url.
'swf/dewplayer.swf?mp3='.
$this->url.
'&showtime=1" /><param name="quality" value="high" /><param name="bgcolor" value="" /><embed src="'.
$rsc_url.
'swf/dewplayer.swf?mp3='.
$this->url.
'&showtime=1" quality="high" bgcolor="" width="200" height="20" name="dewplayer" wmode="transparent" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"></embed></object>';
echo
$params['after_podplayer'];
{ // Not displaying podcast player:
$r .=
'<a href="'.
str_replace( '$url$', $this->url, $params['url_template'] ).
'"';
if( !empty( $params['target'] ) )
$r .=
' target="'.
$params['target'].
'"';
* Template function: Display the number of words in the post
echo (int)
$this->wordcount; // may have been saved as NULL until 1.9
* Template function: Display the number of times the Item has been viewed
* Note: viewcount is incremented whenever the Item's content is displayed with "MORE"
* (i-e full content), see {@link Item::content()}.
* Viewcount is NOT incremented on page reloads and other special cases, see {@link Hit::is_new_view()}
* %d gets replaced in all params by the number of views.
* @param string Link text to display when there are 0 views
* @param string Link text to display when there is 1 views
* @param string Link text to display when there are >1 views
* @return string The phrase about the number of views.
function get_views( $zero =
'#', $one =
'#', $more =
'#' )
$r =
( $zero ==
'#' ?
T_( 'No views' ) :
$zero );
elseif( $this->views ==
1 )
$r =
( $one ==
'#' ?
T_( '1 view' ) :
$one );
$r =
( $more ==
'#' ?
T_( '%d views' ) :
$more );
* Template function: Display a phrase about the number of Item views.
* @param string Link text to display when there are 0 views
* @param string Link text to display when there is 1 views
* @param string Link text to display when there are >1 views (include %d for # of views)
* @return integer Number of views.
function views( $zero =
'#', $one =
'#', $more =
'#' )
* By default, all values will be considered strings
* @todo extra_cat_IDs recording
* @param string parameter name
* @param mixed parameter value
* @param boolean true to set to NULL if empty value
* @return boolean true, if a value has been set; false if it has not changed
function set( $parname, $parvalue, $make_null =
false )
return $this->set_param( $parname, 'number', $parvalue, true );
$r1 =
$this->set_param( 'content', 'string', $parvalue, $make_null );
// Update wordcount as well:
return ( $r1 ||
$r2 ); // return true if one changed
return $this->set_param( 'wordcount', 'number', $parvalue, false );
return $this->set_param( 'datedeadline', 'date', $parvalue, true );
case 'renderers':
// deprecated
return parent::set( $parname, $parvalue, $make_null );
* Set the renderers of the Item.
* @param array List of renderer codes.
* @return boolean true, if it has been set; false if it has not changed
* Set the Author of the Item.
* @param User (Do NOT set to NULL or you may kill the current_User)
* @return boolean true, if it has been set; false if it has not changed
$this->Author =
& $this->creator_User; // deprecated fp> TODO: Test and see if this line can be put once and for all in the constructor
return $this->set( $this->creator_field, $creator_User->ID );
* Create a new Item/Post and insert it into the DB
* This function has to handle all needed DB dependencies!
* @deprecated since EVO_NEXT_VERSION. Use set() + dbinsert() instead
$author_user_ID, // Author
$post_timestamp, // 'Y-m-d H:i:s'
$main_cat_ID =
1, // Main cat ID
$extra_cat_IDs =
array(), // Table of extra cats
$post_status =
'published',
$post_comment_status =
'open',
$post_renderers =
array('default'),
global $DB, $query, $UserCache;
if( $post_locale ==
'#' ) $post_locale =
$default_locale;
// echo 'INSERTING NEW POST ';
if( isset
( $UserCache ) ) // DIRTY HACK
{ // If not in install procedure...
$this->set( $this->creator_field, $author_user_ID );
$this->set( $this->lasteditor_field, $this->{$this->creator_field} );
$this->set( 'title', $post_title );
$this->set( 'urltitle', $post_urltitle );
$this->set( 'content', $post_content );
$this->set( 'datestart', $post_timestamp );
// TODO: dh> $localtimenow is not defined during install! - all sample posts get a last-modified date of 1970-01-01
$this->set( 'datemodified', date('Y-m-d H:i:s',$localtimenow) );
$this->set( 'main_cat_ID', $main_cat_ID );
$this->set( 'extra_cat_IDs', $extra_cat_IDs );
$this->set( 'status', $post_status );
$this->set( 'locale', $post_locale );
$this->set( 'url', $post_url );
$this->set( 'comment_status', $post_comment_status );
$this->set( 'ptyp_ID', $item_typ_ID );
$this->set( 'pst_ID', $item_st_ID );
* Insert object into DB based on previously recorded changes
* @return boolean true on success
global $DB, $current_User, $Plugins;
{ // No creator assigned yet, use current user:
// TODO: allow a plugin to cancel update here (by returning false)?
$Plugins->trigger_event( 'PrependItemInsertTransact', $params =
array( 'Item' =>
& $this ) );
$dbchanges =
$this->dbchanges; // we'll save this for passing it to the plugin hook
if( $result =
parent::dbinsert() )
{ // We could insert the item object..
// Let's handle the extracats:
// Let's handle the tags:
$Plugins->trigger_event( 'AfterItemInsert', $params =
array( 'Item' =>
& $this, 'dbchanges' =>
$dbchanges ) );
* Update the DB based on previously recorded changes
* @return boolean true on success
if( empty($this->urltitle) || isset
($this->dbchanges['post_urltitle']) )
{ // Url title has changed or is empty
// echo 'updating url title';
false, $this->dbprefix, $this->dbIDname, $this->dbtablename ) );
// TODO: dh> allow a plugin to cancel update here (by returning false)?
$Plugins->trigger_event( 'PrependItemUpdateTransact', $params =
array( 'Item' =>
& $this ) );
$dbchanges =
$this->dbchanges; // we'll save this for passing it to the plugin hook
if( $result =
parent::dbupdate() )
{ // We could update the item object..
// Let's handle the extracats:
// Let's handle the extracats:
// Empty pre-rendered content cache - any item property may have influence on it:
$DB->query( 'DELETE FROM T_items__prerendering WHERE itpr_itm_ID = '.
$this->ID );
$Plugins->trigger_event( 'AfterItemUpdate', $params =
array( 'Item' =>
& $this, 'dbchanges' =>
$dbchanges ) );
* Trigger event AfterItemDelete after calling parent method.
* @todo fp> delete related stuff: comments, cats, file links...
* @return boolean true on success
// remember ID, because parent method resets it to 0
if( $r =
parent::dbdelete() )
// Empty pre-rendered content cache:
$DB->query( 'DELETE FROM T_items__prerendering
WHERE itpr_itm_ID = '.
$this->ID );
// re-set the ID for the Plugin event
$Plugins->trigger_event( 'AfterItemDelete', $params =
array( 'Item' =>
& $this ) );
* @param string 'insert' | 'update'
{ // Okay the extra cats are defined:
// delete previous extracats:
$DB->query( 'DELETE FROM T_postcats WHERE postcat_post_ID = '.
$this->ID, 'delete previous extracats' );
$query =
"INSERT INTO T_postcats( postcat_post_ID, postcat_cat_ID ) VALUES ";
//echo "extracat: $extracat_ID <br />";
$query .=
"( $this->ID, $extra_cat_ID ),";
$DB->query( $query, 'insert new extracats' );
* @param string 'insert' | 'update'
if( isset
( $this->tags ) )
{ // Okay the tags are defined:
{ // delete previous tag associations:
// Note: actual tags never get deleted
$DB->query( 'DELETE FROM T_items__itemtag
WHERE itag_itm_ID = '.
$this->ID, 'delete previous tags' );
if( !empty($this->tags) )
// Find the tags that are already in the DB
$query =
'SELECT LOWER( tag_name )
WHERE tag_name IN ('.
$DB->quote($this->tags).
')';
$existing_tags =
$DB->get_col( $query, 0, 'Find existing tags' );
if( !empty( $new_tags ) )
$query =
"INSERT INTO T_items__tag( tag_name ) VALUES ";
foreach( $new_tags as $tag )
$query .=
'( '.
$DB->quote($tag).
' ),';
$DB->query( $query, 'insert new tags' );
$query =
'INSERT INTO T_items__itemtag( itag_itm_ID, itag_tag_ID )
SELECT '.
$this->ID.
', tag_ID
WHERE tag_name IN ('.
$DB->quote($this->tags).
')';
$DB->query( $query, 'Make tag associations!' );
* Increment the view count of the item directly in DB (if the item's Author is not $current_User).
* This method serves TWO purposes (that would break if we used dbupdate() ) :
* - Increment the viewcount WITHOUT affecting the lastmodified date and user.
* - Increment the viewcount in an ATOMIC manner (even if several hits on the same Item occur simultaneously).
* This also triggers the plugin event 'ItemViewsIncreased' if the view count has been increased.
* @return boolean Did we increase view count?
global $Plugins, $DB, $current_User, $Debuglog;
if( isset
( $current_User ) &&
( $current_User->ID ==
$this->creator_user_ID ) )
$Debuglog->add( 'Not incrementing view count, because viewing user is creator of the item.', 'items' );
$DB->query( 'UPDATE T_items__item
SET post_views = post_views + 1
WHERE '.
$this->dbIDname.
' = '.
$this->ID );
// Trigger event that the item's view has been increased
$Plugins->trigger_event( 'ItemViewsIncreased', array( 'Item' =>
& $this ) );
* Get the User who is assigned to the Item.
* @return User|NULLNULL if no user is assigned.
* Get the User who created the Item.
* Get the Blog object for the Item.
* Load the Blog object for the Item, without returning it.
* This is needed for {@link Results} object callbacks.
$this->Blog =
& $BlogCache->get_by_ID( $this->blog_ID );
* Execute or schedule post(=after) processing tasks
* Includes notifications & pings
$notifications_mode =
$Settings->get('outbound_notifications_mode');
if( $notifications_mode ==
'off' )
{ // pings have been done before
$Messages->add( T_('Post had already pinged: skipping notifications...'), 'note' );
{ // pings have been done before
// TODO: Check if issue_date has changed and reschedule
$Messages->add( T_('Post processing already pending...'), 'note' );
if( $this->status !=
'published' )
// TODO: discard any notification that may be pending!
$Messages->add( T_('Post not publicly published: skipping notifications...'), 'note' );
if( $notifications_mode ==
'immediate' )
{ // We want to do the post processing immediately:
// Send email notifications now!
// Record that processing has been done:
$this->set( 'notifications_status', 'finished' );
{ // We want asynchronous post processing:
$Messages->add( T_('Scheduling asynchronous notifications...'), 'note' );
// start datetime. We do not want to ping before the post is effectively published:
$edited_Cronjob->set( 'start_datetime', $this->issue_date );
$edited_Cronjob->set( 'name', sprintf( T_('Send notifications for «%s»'), strip_tags($this->title) ) );
$edited_Cronjob->set( 'controller', 'cron/jobs/_post_notifications.job.php' );
// params: specify which post this job is supposed to send notifications for:
$edited_Cronjob->set( 'params', array( 'item_ID' =>
$this->ID ) );
$edited_Cronjob->dbinsert();
// Memorize the cron job ID which is going to handle this post:
$this->set( 'notifications_ctsk_ID', $edited_Cronjob->ID );
// Record that processing has been scheduled:
$this->set( 'notifications_status', 'todo' );
// Save the new processing status to DB
* Send email notifications to subscribed users
* @todo fp>> shall we notify suscribers of blog were this is in extra-cat? blueyed>> IMHO yes.
if( ! $edited_Blog->get_setting( 'allow_subscriptions' ) )
{ // Subscriptions not enabled!
echo
"<div class=\"panelinfo\">\n";
echo
'<h3>', T_('Notifying subscribed users...'), "</h3>\n";
// Get list of users who want to be notfied:
// TODO: also use extra cats/blogs??
$sql =
'SELECT DISTINCT user_email, user_locale
FROM T_subscriptions INNER JOIN T_users ON sub_user_ID = user_ID
WHERE sub_coll_ID = '.
$this->blog_ID.
'
AND LENGTH(TRIM(user_email)) > 0';
$notify_list =
$DB->get_results( $sql );
// Preprocess list: (this comes form Comment::send_email_notifications() )
foreach( $notify_list as $notification )
$notify_array[$notification->user_email] =
$notification->user_locale;
if( empty($notify_array) )
echo
'<p>', T_('No-one to notify.'), "</p>\n</div>\n";
* We have a list of email addresses to notify:
$cache_by_locale =
array();
foreach( $notify_array as $notify_email =>
$notify_locale )
if( ! isset
($cache_by_locale[$notify_locale]) )
{ // No message for this locale generated yet:
// Calculate length for str_pad to align labels:
$cache_by_locale[$notify_locale]['subject'] =
sprintf( T_('[%s] New post: "%s"'), $edited_Blog->get('shortname'), $this->get('title') );
$cache_by_locale[$notify_locale]['message'] =
str_pad( T_('Blog'), $pad_len ).
': '.
$edited_Blog->get('shortname')
.
' ( '.
str_replace('&', '&', $edited_Blog->gen_blogurl()).
" )\n"
.
str_pad( T_('Title'), $pad_len ).
': '.
$this->get('title').
"\n"
// linked URL or "-" if empty:
.
str_pad( T_('Content'), $pad_len ).
': '
// TODO: We MAY want to force a short URL and avoid it to wrap on a new line in the mail which may prevent people from clicking
// TODO: might get moved onto a single line, at the end of the content..
.
$this->get('content').
"\n"
.
T_('Edit/Delete').
': '.
$admin_url.
'?ctrl=items&blog='.
$this->blog_ID.
'&p='.
$this->ID.
"\n\n"
.
T_('Edit your subscriptions/notifications').
': '.
str_replace('&', '&', url_add_param( $edited_Blog->gen_blogurl(), 'disp=subs' ) ).
"\n";
locale_restore_previous();
if( $display ) echo
T_('Notifying:').
$notify_email.
"<br />\n";
echo
"<p>Sending notification to $notify_email:<pre>$cache_by_locale[$notify_locale]['message']</pre>";
send_mail( $notify_email, $cache_by_locale[$notify_locale]['subject'], $cache_by_locale[$notify_locale]['message'], $mail_from );
if( $display ) echo
'<p>', T_('Done.'), "</p>\n</div>\n";
* Send outbound pings for a post
if( preg_match( '#^http://localhost[/:]#', $baseurl)
||
preg_match( '~^\w+://[^/]+\.local/~', $baseurl ) /* domain ending in ".local" */ )
$Messages->add( T_('Skipping pings (Running on localhost).'), 'note' );
else foreach( $ping_plugins as $plugin_code )
$Plugin =
& $Plugins->get_by_code($plugin_code);
$Messages->add( sprintf(T_('Pinging %s...'), $Plugin->ping_service_name), 'note' );
$params =
array( 'Item' =>
& $this, 'xmlrpcresp' =>
NULL, 'display' =>
false );
$r =
$Plugin->ItemSendPing( $params );
if( isset
($params['xmlrpcresp']) &&
is_a($params['xmlrpcresp'], 'xmlrpcresp') )
// dh> TODO: let xmlrpc_displayresult() handle $Messages (e.g. "error", but should be connected/after the "Pinging %s..." from above)
* Callback user for footer()
* Get a member param by its name
* @param mixed Name of parameter
* @return mixed Value of parameter
return T_( $post_statuses[$this->status] );
$ItemStatusCache =
& get_Cache( 'ItemStatusCache' );
if( ! ($Element =
& $ItemStatusCache->get_by_ID( $this->pst_ID, true, false ) ) )
return $Element->get_name();
$ItemTypeCache =
& get_Cache( 'ItemTypeCache' );
$type_Element =
& $ItemTypeCache->get_by_ID( $this->ptyp_ID );
return $type_Element->get_name();
// Deprecated by fp 2006-08-21
return ($this->post_notifications_status ==
'finished');
return parent::get( $parname );
* Assign the item to the first category we find in the requested collection
* @param integer $collection_ID
// Get the first category ID for the collection ID param
$cat_ID =
$DB->get_var( '
WHERE cat_blog_ID = '.
$collection_ID.
'
// Set to the item the first category we got
$this->set( 'main_cat_ID', $cat_ID );
* Get the list of renderers for this Item.
* Get the list of validated renderers for this Item. This includes stealth plugins etc.
* @return array List of validated renderer codes
if( ! isset
($this->renderers_validated) )
$Plugins_admin =
& get_Cache('Plugins_admin');
$this->renderers_validated =
$Plugins_admin->validate_renderer_list( $this->get_renderers() );
return $this->renderers_validated;
* Add a renderer (by code) to the Item.
* @param string Renderer code to add for this item
* @return boolean True if renderers have changed
if( in_array( $renderer_code, $renderers ) )
$renderers[] =
$renderer_code;
$this->renderers_validated =
NULL;
//echo 'Added renderer '.$renderer_code;
* Remove a renderer (by code) from the Item.
* @param string Renderer code to remove for this item
* @return boolean True if renderers have changed
while( ( $key =
array_search( $renderer_code, $renderers ) ) !==
false )
$this->renderers_validated =
NULL;
//echo 'Removed renderer '.$renderer_code;
* $Log: _item.class.php,v $
* Revision 1.32.2.8 2009/06/13 18:50:47 tblue246
* [MFH] Item::set_tags_from_string(): Fixed SQL error in combination with umlauts (same umlaut, different case)
* Revision 1.32.2.7 2009/01/29 15:54:51 tblue246
* Use 1 as the default post type ID when creating a new Item object. Fixes http://forums.b2evolution.net/viewtopic.php?t=17780 .
* Revision 1.32.2.6 2008/12/22 01:19:53 fplanque
* Revision 1.32.2.5 2008/12/06 14:07:00 tblue246
* RDF trackback autodiscovery: Display post title properly (reported by Austriaco, see http://forums.b2evolution.net/viewtopic.php?t=16205 ).
* Revision 1.32.2.4 2008/09/15 10:44:45 fplanque
* Revision 1.32.2.3 2008/04/26 22:28:53 fplanque
* Revision 1.32.2.2 2008/04/12 19:34:03 fplanque
* Revision 1.32.2.1 2008/02/18 20:23:20 fplanque
* Revision 1.32 2008/01/23 12:51:20 fplanque
* posts now have divs with IDs
* Revision 1.31 2008/01/21 09:35:31 fplanque
* Revision 1.30 2008/01/20 18:20:26 fplanque
* Antispam per group setting
* Revision 1.29 2008/01/19 15:45:28 fplanque
* Revision 1.28 2008/01/18 15:53:42 fplanque
* Revision 1.27 2008/01/17 14:38:30 fplanque
* Item Footer template tag
* Revision 1.26 2008/01/14 07:22:07 fplanque
* Revision 1.25 2008/01/08 03:31:51 fplanque
* Revision 1.24 2008/01/07 02:53:27 fplanque
* Revision 1.23 2007/12/08 14:43:36 yabs
* bugfix ( http://forums.b2evolution.net/viewtopic.php?t=13482 )
* Revision 1.22 2007/11/30 21:37:29 fplanque
* Revision 1.21 2007/11/29 22:47:12 fplanque
* tags everywhere + debug
* Revision 1.20 2007/11/29 20:53:45 fplanque
* Fixed missing url link in basically all skins ...
* Revision 1.19 2007/11/25 14:28:17 fplanque
* additional SEO settings
* Revision 1.18 2007/11/24 21:24:14 fplanque
* display tags in backoffice
* Revision 1.17 2007/11/22 17:53:39 fplanque
* filemanager display cleanup, especially in IE (not perfect)
* Revision 1.16 2007/11/15 23:45:41 blueyed
* (Re-)Added phpdoc for get_edit_link
* Revision 1.15 2007/11/04 01:10:57 fplanque
* Revision 1.14 2007/11/03 23:54:38 fplanque
* Revision 1.13 2007/11/03 21:04:26 fplanque
* Revision 1.12 2007/11/02 01:54:46 fplanque
* Revision 1.11 2007/09/13 19:16:14 fplanque
* feedback_link() cleanup
* Revision 1.10 2007/09/13 02:37:22 fplanque
* Revision 1.9 2007/09/11 23:10:39 fplanque
* Revision 1.8 2007/09/10 14:53:04 fplanque
* Revision 1.7 2007/09/09 12:51:58 fplanque
* Revision 1.6 2007/09/09 09:15:59 yabs
* Revision 1.5 2007/09/08 19:31:28 fplanque
* cleanup of XML feeds for comments on individual posts.
* Revision 1.4 2007/09/04 22:16:33 fplanque
* in context editing of posts
* Revision 1.3 2007/08/28 02:43:40 waltercruz
* Template function to get the rss link to the feeds of the comments on each post
* Revision 1.2 2007/07/03 23:21:32 blueyed
* Fixed includes/requires in/for tests
* Revision 1.1 2007/06/25 11:00:24 fplanque
* MODULES (refactored MVC)
* Revision 1.184 2007/06/24 22:26:34 fplanque
* improved feedback template
* Revision 1.183 2007/06/21 00:44:37 fplanque
* Revision 1.182 2007/06/20 21:42:13 fplanque
* implemented working widget/plugin params
* Revision 1.180 2007/06/18 20:59:55 fplanque
* do not display link to comments if comments are disabled
* Revision 1.179 2007/06/13 23:29:02 fplanque
* Revision 1.178 2007/06/11 01:55:57 fplanque
* level based user permissions
* Revision 1.177 2007/05/28 15:18:30 fplanque
* Revision 1.176 2007/05/28 01:33:22 fplanque
* Revision 1.175 2007/05/27 00:35:26 fplanque
* tag display + tag filtering
* Revision 1.174 2007/05/20 01:01:35 fplanque
* make trackback silent when it should be
* Revision 1.173 2007/05/14 02:47:23 fplanque
* (not so) basic Tags framework
* Revision 1.172 2007/05/13 22:02:07 fplanque
* removed bloated $object_def
* Revision 1.171 2007/04/26 00:11:11 fplanque
* Revision 1.170 2007/04/15 13:34:36 blueyed
* Fixed default $url generation in page_links()
* Revision 1.169 2007/04/05 22:57:33 fplanque
* Added hook: UnfilterItemContents
* Revision 1.168 2007/03/31 22:46:46 fplanque
* FilterItemContent event
* Revision 1.167 2007/03/26 12:59:18 fplanque
* Revision 1.166 2007/03/25 10:19:30 fplanque
* Revision 1.165 2007/03/24 20:41:16 fplanque
* Refactored a lot of the link junk.
* Made options blog specific.
* Some junk still needs to be cleaned out. Will do asap.
* Revision 1.164 2007/03/19 23:59:32 fplanque
* Revision 1.163 2007/03/18 03:43:19 fplanque
* Splitting Item/ItemLight and ItemList/ItemListLight
* Goal: Handle Items with less footprint than with their full content
* (will be even worse with multiple languages/revisions per Item)
* Revision 1.162 2007/03/11 23:57:07 fplanque
* item editing: allow setting to 'redirected' status
* Revision 1.161 2007/03/06 12:18:08 fplanque
* got rid of dirty Item::content()
* Advantage: the more link is now independant. it can be put werever people want it
* Revision 1.160 2007/03/05 04:52:42 fplanque
* better precision for viewcounts
* Revision 1.159 2007/03/05 04:49:17 fplanque
* better precision for viewcounts
* Revision 1.158 2007/03/05 02:13:26 fplanque
* Revision 1.157 2007/03/05 01:47:50 fplanque
* splitting up Item::content() - proof of concept.
* Revision 1.156 2007/03/03 01:14:11 fplanque
* new methods for navigating through posts in single item display mode
* Revision 1.155 2007/03/02 04:40:38 fplanque
* fixed/commented a lot of stuff with the feeds
* Revision 1.154 2007/03/02 03:09:12 fplanque
* rss length doesn't make sense since it doesn't apply to html format anyway.
* clean solutionwould be to handle an "excerpt" field separately
* Revision 1.153 2007/02/23 19:16:07 blueyed
* MFB: Fixed handling of Item::content for pre-rendering (it gets passed by reference!)
* Revision 1.152 2007/02/18 22:51:26 waltercruz
* Fixing a little confusion with quotes and string concatenation
* Revision 1.151 2007/02/08 03:45:40 waltercruz
* Changing double quotes to single quotes
* Revision 1.150 2007/02/05 13:32:49 waltercruz
* Changing double quotes to single quotes
* Revision 1.149 2007/01/26 04:52:53 fplanque
* clean comment popups (skins 2.0)
* Revision 1.148 2007/01/26 02:12:06 fplanque
* Revision 1.147 2007/01/23 03:46:24 fplanque
* cleaned up presentation
* Revision 1.146 2007/01/19 10:45:42 fplanque
* At this point the photoblogging code can be considered operational.
* Revision 1.145 2007/01/11 19:29:50 blueyed
* Fixed E_NOTICE when using the "excerpt" feature
* Revision 1.144 2006/12/26 00:08:29 fplanque
* Revision 1.143 2006/12/21 22:35:28 fplanque
* No regression. But a change in usage. The more link must be configured in the skin.
* Renderers cannot side-effect on the more tag any more and that actually makes the whole thing safer.
* Revision 1.142 2006/12/20 13:57:34 blueyed
* TODO about regression because of pre-rendering and the <!--more--> tag
* Revision 1.141 2006/12/18 13:31:12 fplanque
* Revision 1.140 2006/12/16 01:30:46 fplanque
* Setting to allow/disable email subscriptions on a per blog basis
* Revision 1.139 2006/12/15 22:59:05 fplanque
* Revision 1.138 2006/12/14 22:26:31 blueyed
* Fixed E_NOTICE and displaying of pings into $Messages (though "hackish")
* Revision 1.137 2006/12/12 02:53:56 fplanque
* Activated new item/comments controllers + new editing navigation
* Some things are unfinished yet. Other things may need more testing.
* Revision 1.136 2006/12/07 23:13:11 fplanque
* @var needs to have only one argument: the variable type
* Otherwise, I can't code!
* Revision 1.135 2006/12/06 23:55:53 fplanque
* hidden the dead body of the sidebar plugin + doc
* Revision 1.134 2006/12/05 14:28:29 blueyed
* Fixed wordcount==0 handling; has been saved as NULL
* Revision 1.133 2006/12/05 06:38:40 blueyed
* Revision 1.132 2006/12/05 00:39:56 fplanque
* fixed some more permalinks/archive links
* Revision 1.131 2006/12/05 00:34:39 blueyed
* Implemented custom "None" option text in DataObjectCache; Added for $ItemStatusCache, $GroupCache, UserCache and BlogCache; Added custom text for Item::priority_options()
* Revision 1.130 2006/12/04 20:52:40 blueyed
* Revision 1.129 2006/12/04 19:57:58 fplanque
* How often must I fix the weekly archives until they stop bugging me?
* Revision 1.128 2006/12/04 19:41:11 fplanque
* Each blog can now have its own "archive mode" settings
* Revision 1.127 2006/12/03 18:15:32 fplanque
* Revision 1.126 2006/12/01 20:04:31 blueyed
* Renamed Plugins_admin::validate_list() to validate_renderer_list()
* Revision 1.125 2006/12/01 19:46:42 blueyed
* Moved Plugins::validate_list() to Plugins_admin class; added stub in Plugins, because at least the starrating_plugin uses it
* Revision 1.124 2006/11/28 20:04:11 blueyed
* No edit link, if ID==0 to avoid confusion in preview, see http://forums.b2evolution.net/viewtopic.php?p=47422#47422
* Revision 1.123 2006/11/24 18:27:24 blueyed
* Fixed link to b2evo CVS browsing interface in file docblocks
* Revision 1.122 2006/11/22 20:48:58 blueyed
* Added Item::get_Chapters() and Item::get_main_Chapter(); refactorized
* Revision 1.121 2006/11/22 20:12:18 blueyed
* Use $format param in Item::categories()
* Revision 1.120 2006/11/19 22:17:42 fplanque
* Revision 1.119 2006/11/19 16:07:31 blueyed
* Fixed saving empty renderers list. This should also fix the saving of "default" instead of the explicit renderer list
* Revision 1.118 2006/11/17 18:36:23 blueyed
* dbchanges param for AfterItemUpdate, AfterItemInsert, AfterCommentUpdate and AfterCommentInsert
* Revision 1.117 2006/11/13 20:49:52 fplanque
* Revision 1.116 2006/11/10 20:14:11 blueyed
* Revision 1.115 2006/11/02 16:12:49 blueyed
* Revision 1.114 2006/11/02 16:01:00 blueyed
* Revision 1.113 2006/10/29 18:33:23 blueyed
* Revision 1.112 2006/10/23 22:19:02 blueyed
* Fixed/unified encoding of redirect_to param. Use just rawurlencode() and no funky & replacements
* Revision 1.111 2006/10/18 00:03:51 blueyed
* Some forgotten url_rel_to_same_host() additions