Source for file _blog_main.inc.php
Documentation is available at _blog_main.inc.php
* This file loads and initializes the blog to be displayed.
* This file is part of the Quam Plures project - {@link http://quamplures.net/}.
* See also {@link https://launchpad.net/quam-plures}.
* @copyright (c) 2009 - 2011 by the Quam Plures developers - {@link http://quamplures.net/}
* @copyright (c)2003-2009 by Francois PLANQUE - {@link http://fplanque.net/}.
* Parts of this file are copyright (c)2004-2005 by Daniel HAHLER - {@link http://thequod.de/contact}.
* @license http://quamplures.net/license.html GNU General Public License (GPL)
* {@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
if( !defined('QP_CONFIG_LOADED') ) die( 'Please, do not access this page directly.' );
require_once dirname(__FILE__ ). '/_main.inc.php';
$Timer->start( '_blog_main.inc' );
* blog ID. This is a little bit special.
* In most cases $blog should be set by a stub file and the param() call below will just check that it's an integer.
* Note we do NOT memorize the param as we don't want it in regenerate_url() calls.
* Whenever we do, index.php will already have called param() with memorize=true
* In some cases $blog will not have been set before and it will be set with the param() call below.
* Currently, this only happens with the old /qp_srvc/ RSS stubs.
param( 'blog', 'integer', '', false );
// Getting current blog info:
$Blog = & $BlogCache->get_by_ID( $blog, false, false );
require $templates_path. '_404_blog_not_found.main.php'; // error & exit
param( 'disp', 'string', 'posts', true );
* _______________________________ Locale / Charset for the Blog _________________________________
TODO: blueyed>> This should get moved as default to the locale detection in _main.inc.php,
as we only want to activate the I/O charset, which is probably the user's..
It prevents using a locale/charset in the front office, apart from the one given as default for the blog!!
fp>there is no blog defined in _main and there should not be any
blueyed> Sure, but that means we should either split it, or use the locale here only, if there's no-one given with higher priority.
// Activate matching locale:
$Debuglog->add( 'Activating blog locale: '. $Blog->get('locale'), 'locale' );
// Re-Init charset handling, in case current_charset has changed:
// Reload Blog(s) (for encoding of name, tagline etc):
$Blog = & $BlogCache->get_by_ID( $blog );
* _____________________________ Extra path info decoding ________________________________
* This will translate extra path into 'regular' params.
* Decoding should try to work like this:
* baseurl/blog-urlname/junk/.../junk/post-title -> points to a single post (no ending slash)
* baseurl/blog-urlname/junk/.../junk/p142 -> points to a single post
* baseurl/blog-urlname/2006/ -> points to a yearly archive because of ending slash + 4 digits
* baseurl/blog-urlname/2006/12/ -> points to a monthly archive
* baseurl/blog-urlname/2006/12/31/ -> points to a daily archive
* baseurl/blog-urlname/2006/w53/ -> points to a weekly archive
* baseurl/blog-urlname/junk/.../junk/chap-urlname/ -> points to a single chapter/category (because of ending slash)
* Note: category names cannot be named like this [a-z][0-9]+
if( ! isset ( $resolve_extra_path ) ) { $resolve_extra_path = true; }
if( $resolve_extra_path )
// Check and Remove blog base URI from ReqPath:
$blog_baseuri = substr( $Blog->gen_baseurl(), strlen( $Blog->get('baseurlroot') ) );
$Debuglog->add( 'blog_baseuri: "'. $blog_baseuri. '"', 'params' );
$blog_baseuri_regexp = preg_replace( '¤(\.php[0-9]?)?/?$¤', '', $blog_baseuri );
// Readd possibilities in order to get a broad match:
$blog_baseuri_regexp = '¤^'. preg_quote( $blog_baseuri_regexp ). '(\.php[0-9]?)?/(.+)$¤';
if( preg_match( $blog_baseuri_regexp, $ReqPath, $matches ) )
{ // We have extra path info
$path_string = $matches[2];
$Debuglog->add( 'Extra path info found! path_string=' . $path_string , 'params' );
// Replace encoded ";" and ":" with regular chars (used for tags)
// TODO: dh> why not urldecode it altogether? fp> would prolly make sense but requires testing -- note: check with tags (move urldecode from tags up here)
// TODO: PHP5: use str_ireplace
array('%3b', '%3B', '%3a', '%3A'),
array(';', ';', ':', ':'),
$path_elements = preg_split( '~/~', $path_string, 20, PREG_SPLIT_NO_EMPTY );
if( isset ( $path_elements[0] )
&& ( $path_elements[0] == $Blog->stub
|| $path_elements[0] == $Blog->urlname
|| $path_elements[0] == 'index.php' ) )
{ // Ignore stub files, blog url names, and index.php
$Debuglog->add( 'Ignoring stub filename, blog urlname, or index.php in extra path info' , 'params' );
if( isset ( $path_elements[0] )
&& is_file( $basepath. $path_elements[0] ) )
{ // Ignore filenames that exist in root
$Debuglog->add( 'Ignoring filenames that exist as installation root files in extra path info' , 'params' );
// Do we still have extra path info to decode?
if( count($path_elements) )
// TODO: dh> add plugin hook here, which would allow to handle path elements (name spaces in clean URLs), and to override internal functionality (e.g. handle tags in a different way).
// Is this a tag ("prefix-only" mode)?
if( $Blog->get_setting('tag_links') == 'prefix-only'
&& count($path_elements) == 2
&& $path_elements[0] == $Blog->get_setting('tag_prefix')
&& isset ($path_elements[1]) )
// # of posts per page for tag page:
if( ! $posts = $Blog->get_setting( 'tag_posts_per_page' ) )
$posts = $Blog->get_setting( 'posts_per_page' );
// Does the pathinfo end with a / or a ; ?
$last_char = substr( $path_string, - 1 );
$last_part = $path_elements[count( $path_elements )- 1];
$last_len = strlen( $last_part );
if( ( $last_char == '-' && ( ! $tags_dash_fix || $last_len != 40 ) ) || $last_char == ':'|| $last_char == ';' )
{ // - : or ; -> We'll consider this to be a tag page
$tag = substr( $last_part, 0, - 1 );
if( ! $posts = $Blog->get_setting( 'tag_posts_per_page' ) )
$posts = $Blog->get_setting( 'posts_per_page' );
elseif( ( $tags_dash_fix && $last_char == '-' && $last_len == 40 ) || $last_char != '/' )
{ // NO ENDING SLASH or ends with a dash, is 40 chars long and $tags_dash_fix is true
// -> We'll consider this to be a ref to a post.
$Debuglog->add( 'We consider this o be a ref to a post - last char: '. $last_char, 'params' );
// Set a lot of defaults as if we had received a complex URL:
$more = 1; // Display the extended entries' text
$c = 1; // Display comments
$tb = 1; // Display trackbacks
if( preg_match( '#^p([0-9]+)$#', $last_part, $req_post ) )
{ // The last param is of the form p000
$p = $req_post[1]; // Post to display
{ // Last param is a string, we'll consider this to be a post urltitle
{ // ENDING SLASH -> we are looking for a daterange OR a chapter:
$Debuglog->add( 'Last part: '. $last_part , 'params' );
{ // Last part is a number or a "week" number:
$Debuglog->add( 'Last part is a number or a "week" number: '. $path_elements[$i] , 'params' );
if( isset ( $path_elements[$i] ) )
{ // We'll consider this to be the year
$m = $path_elements[$i++ ];
$Debuglog->add( 'Setting year from extra path info. $m=' . $m , 'params' );
// Also use the prefered posts per page for archives (may be NULL, in which case the blog default will be used later on)
if( ! $posts = $Blog->get_setting( 'archive_posts_per_page' ) )
$posts = $Blog->get_setting( 'posts_per_page' );
if( isset ( $path_elements[$i] ) && is_numeric( $path_elements[$i] ) )
{ // We'll consider this to be the month
$m .= $path_elements[$i++ ];
$Debuglog->add( 'Setting month from extra path info. $m=' . $m , 'params' );
if( isset ( $path_elements[$i] ) && is_numeric( $path_elements[$i] ) )
{ // We'll consider this to be the day
$m .= $path_elements[$i++ ];
$Debuglog->add( 'Setting day from extra path info. $m=' . $m , 'params' );
elseif( isset ( $path_elements[$i] ) && substr( $path_elements[$i], 0, 1 ) == 'w' )
{ // We consider this a week number
$w = substr( $path_elements[$i], 1, 2 );
{ // We did not get a number/year...
$disp_detail = '404-malformed_url-missing_year';
elseif( preg_match( '|^[A-Za-z0-9\-_]+$|', $last_part ) ) // UNDERSCORES for catching OLD URLS!!!
{ // We are pointing to a chapter/category:
$ChapterCache = & get_Cache( 'ChapterCache' );
$Chapter = & $ChapterCache->get_by_urlname( $last_part, false );
{ // We could not match a chapter...
// We are going to consider this to be a post title with a misplaced trailing slash.
// That happens when upgrading from WP for example.
$title = $last_part; // Will be sought later
$already_looked_into_chapters = true;
{ // We could match a chapter from the extra path:
// Also use the prefered posts per page for a cat
if( ! $posts = $Blog->get_setting( 'chapter_posts_per_page' ) )
$posts = $Blog->get_setting( 'posts_per_page' );
{ // We did not get anything we can decode...
$disp_detail = '404-malformed_url-bad_char';
* ____________________________ Query params ____________________________
* Note: if the params have been set by the extra-path-info above, param() will not touch them.
param( 'p', 'integer', '', true ); // Specific post number to display
param( 'title', 'string', '', true ); // urtitle of post to display
param( 'redir', 'string', 'yes', false ); // Do we allow redirection to canonical URL? (allows to force a 'single post' URL for commenting)
param( 'preview', 'integer', 0, true ); // Is this preview ?
param( 'stats', 'integer', 0 ); // Deprecated but might still be used by spambots
param( 'cat', 'integer', '', true ); // Chapter ID
// In case these were not set by the stub:
if( !isset ($timestamp_min) ) $timestamp_min = '';
if( !isset ($timestamp_max) ) $timestamp_max = '';
* ____________________________ Get specific Item if requested ____________________________
if( !empty($p) || !empty($title) )
{ // We are going to display a single post
// Make sure the single post we're requesting (still) exists:
$Item = & $ItemCache->get_by_ID( $p, false );
{ // Get from post title:
$Item = & $ItemCache->get_by_urltitle( $title, false );
// fp> TODO: ->viewing_allowed() for draft, private, protected and deprecated...
$tag_fallback = ( $tags_dash_fix && substr( $orig_title, - 1 ) == '-' && strlen( $orig_title ) == 40 );
if( ! $tag_fallback && !empty($title) && empty($already_looked_into_chapters) )
{ // Let's try to fall back to a category/chapter...
$ChapterCache = & get_Cache( 'ChapterCache' );
$Chapter = & $ChapterCache->get_by_urlname( $title, false );
{ // We could match a chapter from the extra path:
// Also use the prefered posts per page for a cat
if( ! $posts = $Blog->get_setting( 'chapter_posts_per_page' ) )
$posts = $Blog->get_setting( 'posts_per_page' );
{ // Let's try to fall back to a tag...
$title = substr( $orig_title, 0, - 1 );
if( $Blog->get_tag_post_count( $title ) )
{ // We could match a tag from the extra path:
{ // We were not able to fallback to anythign meaningful:
$disp_detail = '404-post_not_found';
else if( ! empty( $cat ) )
{ // We are pointing to a chapter/category (by ID):
$ChapterCache = & get_Cache( 'ChapterCache' );
$Chapter = & $ChapterCache->get_by_ID( $cat, false );
{ // Category not found, bail out:
$disp_detail = '404-category-not-found';
* ____________________________ "Clean up" the request ____________________________
* 1) disp is set to "single" if single post requested
* 2) URL is canonical if:
* - some content was requested in a weird/deprecated way
* - or if content identifiers have changed
if( $stats || $disp == 'stats' )
{ // This used to be a spamfest...
require $templates_path. '_410_stats_gone.main.php'; // error & exit
elseif( !empty($preview) )
// Consider this as an admin hit!
$Hit->referer_type = 'admin';
elseif( $disp == 'posts' && !empty($Item) )
{ // We are going to display a single post
// if( in_array( $Item->ptyp_ID, array( 1000, 1500, 1520, 1530, 1570 ) ) ) // pages and intros
if( $Item->ptyp_ID == 1000 )
// fp> note: the redirecting code that was here moved to template_init() with the other redirecting code.
// That feels more consistent and may also allow some templates to handle redirects differently (framing?)
// I hope I didn't screw that up... but it felt like the historical reasons for this to be here no longer applied.
* ______________________ DETERMINE WHICH TEMPLATE TO USE FOR DISPLAY _______________________
* Check if a temporary template has been requested (used for RSS syndication
* This will be handled like any other template.
* tempskin is also checked for b2evolution backward compatibility.
* @todo maybe restrict that to authorized users?
if( param( 'viewmode', 'string', param( 'tempskin', 'string' ), true ) !== '' )
{ // A template has been requested by folder_name (url or stub):
// Check validity of requested template name:
if( preg_match( '~([^-A-Za-z0-9._]|\.\.)~', $template ) )
debug_die( 'The requested template name is invalid.' );
$TemplateCache = & get_cache( 'TemplateCache' );
$Template = & $TemplateCache->new_obj( NULL, $template );
if( $Template->type == 'feed' )
{ // Check if we actually allow the display of the feed; last chance to revert to 404 displayed in default template.
// Note: Templates with the type "feed" can always be accessed, even when they're not installed.
if( $Blog->get_setting('feed_content') == 'none' )
{ // We don't want to provide feeds; revert to 404!
$disp_detail = '404-feeds-disabled';
{ // The requested template is not a feed template and exists in the file system, but isn't installed:
else if( ! empty( $viewmode ) )
{ // By definition, we want to see the temporary template (if we don't use feedburner... )
if( !isset ( $template ) && !empty($Blog->template_ID) ) // Note: if $template is set to '', then we want to do a "no template" display
{ // Use default template from the database
$TemplateCache = & get_cache( 'TemplateCache' );
$Template = & $TemplateCache->get_by_ID( $Blog->template_ID );
$template = $Template->folder;
// Because a lot of bloggers will delete templates, we have to make this fool proof with extra checking:
{ // We want to use a template, but it doesn't exist!
$err_msg = sprintf( T_('The template [%s] set for blog [%s] does not exist. It must be properly set in the <a %s>blog properties</a> or properly overriden in a stub file.'),
$Blog->dget('shortname'),
'href="'. $admin_url. '?ctrl=coll_settings&tab=template&blog='. $Blog->ID. '"' );
$Timer->pause( '_blog_main.inc');
* _______________________________ READY TO DISPLAY _______________________________
* At this point $template holds the name of the template we want to use, or '' for no template!
// fp> TODO: please doc with example of what this can be used for
$Plugins->trigger_event( 'BeforeBlogDisplay', array('template'=> $template) );
if( !empty( $template ) )
{ // We want to display with a template now:
// Instanciate PageCache:
// Check for cached content & Start caching if needed
// Note: there are some redirects inside the templates themselves for canonical URLs,
// If we have a cache hit, the redirect won't take place until the cache expires -- probably ok.
// If we start collecting and a redirect happens, the collecting will just be lost and that's what we want.
if( ! $PageCache->check() )
{ // Cache miss, we have to generate:
$Plugins->call_method( $template_provided_by_plugin, 'DisplayTemplate', $tmp_params = array('template'=> $template) );
// Path for the current template:
$ads_current_template_path = $templates_path. $template. '/';
'404' => '404_not_found.main.php',
'arcdir' => 'arcdir.main.php',
'catdir' => 'catdir.main.php',
'comments' => 'comments.main.php',
'credits' => 'credits.main.php',
'feedback-popup' => 'feedback_popup.main.php',
'mediaidx' => 'mediaidx.main.php',
'msgform' => 'msgform.main.php',
'page' => 'page.main.php',
'posts' => 'posts.main.php',
'profile' => 'profile.main.php',
'single' => 'single.main.php',
'subs' => 'subs.main.php',
// All others will default to index.main.php
if( !empty($disp_handlers[$disp]) )
if( file_exists( $disp_handler = $ads_current_template_path. $disp_handlers[$disp] ) )
{ // The template has a customized page handler for this display:
$Debuglog->add('blog_main: include '. rel_path_to_base($disp_handler). ' (custom to this template)', 'template');
{ // Use the default handler from the templates dir:
$Debuglog->add('blog_main: include '. rel_path_to_base($ads_current_template_path. 'index.main.php'). ' (default handler)', 'template');
require $ads_current_template_path. 'index.main.php';
{ // Use the default handler from the templates dir:
$Debuglog->add('blog_main: include '. rel_path_to_base($ads_current_template_path. 'index.main.php'). ' (default index handler)', 'template');
require $ads_current_template_path. 'index.main.php';
// Save collected cached data if needed:
$PageCache->end_collect();
// We probably don't want to return to the caller if we have displayed a template...
// That is useful if the caller implements a custom display but we still use templates for RSS/ Atom etc..
{ // We don't use a template. Hopefully the caller will do some displaying.
// Set a few vars with default values, just in case...
$ads_current_template_path = $srvc_path;
// We'll just return to the caller now... (if we have not used a template, the caller should do the display after this)
|