<?php
/**
* This file loads and initializes the blog to be displayed.
*
* This file is part of the b2evolution/evocms project - {@link http://b2evolution.net/}.
* See also {@link https://github.com/b2evolution/b2evolution}.
*
* @license GNU GPL v2 - {@link http://b2evolution.net/about/gnu-gpl-license}
*
* @copyright (c)2003-2020 by Francois Planque - {@link http://fplanque.com/}.
* Parts of this file are copyright (c)2004-2005 by Daniel HAHLER - {@link http://thequod.de/contact}.
*
* @package main
*/
if( !defined('EVO_CONFIG_LOADED') ) die( 'Please, do not access this page directly.' );
/**
* Initialize everything:
*/
require_once dirname(__FILE__).'/_main.inc.php';
load_funcs('skins/_skin.funcs.php');
load_class( 'items/model/_itemlist.class.php', 'ItemList' );
// fp> A lot of time (like 40ms) will be consumed the first time a new Blog object is created.
// This happens within _BLOG_MAIN bloc in non logged in mode and is pretty much perturbing.
// Trying to create a dummy Blog below move the delay out.
// The constructor doesn't consume any time at all.
// This is very strange. Is it because of recursive class loading that happens when instanciating a Blog?
//$dummy = new Blog();
$Timer->start( '_BLOG_MAIN.inc' );
// Evo toolbar visibility:
// true - (Default) Visible if current user has a permission to view toolbar,
// false - Hidden and it is not printed at all,
// 'hidden' - Toolbar is printed out but it is hidden with css property.
// (Used for customizer mode when we should grab toolbar from iframe to main window)
param( 'show_toolbar', 'string', NULL );
if( $show_toolbar !== NULL && $show_toolbar !== 'hidden' )
{ // Convert all not string possible values to boolean type:
$show_toolbar = (boolean)$show_toolbar;
}
/*
* 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 /xmlsrv/ RSS stubs.
*/
param( 'blog', 'integer', '', false );
// Getting current blog info:
$BlogCache = & get_BlogCache();
/**
* @var Blog
*/
$Collection = $Blog = & $BlogCache->get_by_ID( $blog, false, false );
if( empty( $Blog ) )
{
siteskin_init();
siteskin_include( '_404_blog_not_found.main.php' ); // error
exit(0);
// EXIT.
}
if( isset( $ReqURL ) )
{ // Check protocol of the current URL:
if( $Blog->get_setting( 'http_protocol' ) == 'always_http' )
{ // Check current URL is really using HTTP protocol as it is specified with collection setting "SSL" = "Always use http":
if( strpos( $ReqURL, 'https://' ) === 0 )
{ // Redirect to URL with allowed protocol because current URL has a protocol HTTPS instead of expected HTTP:
$fixed_req_url = 'http'.substr( $ReqURL, 5 );
$Debuglog->add( 'Redirecting to HTTP URL ['.$fixed_req_url.'] because of collection setting "SSL".' );
header_redirect( $fixed_req_url, true ); // Redirect with permanent status 301
// EXITED.
}
}
elseif( $Blog->get_setting( 'http_protocol' ) == 'always_https' )
{ // Check current URL is really using HTTPS protocol as it is specified with collection setting "SSL" = "Always use https":
if( strpos( $ReqURL, 'http://' ) === 0 )
{ // Redirect to URL with allowed protocol because current URL has a protocol HTTP instead of expected HTTPS:
$fixed_req_url = 'https'.substr( $ReqURL, 4 );
$Debuglog->add( 'Redirecting to HTTPS URL ['.$fixed_req_url.'] because of collection setting "SSL".' );
header_redirect( $fixed_req_url, true ); // Redirect with permanent status 301
// EXITED.
}
}
}
// Set a selected collection in user settings in order to use a correct last viewed collection URL in back-office:
set_working_blog( $blog );
// Do we allow redirection to canonical URL? (allows to force a 'single post' URL for commenting)
param( 'redir', 'string', 'yes', false );
// Initialize modes to debug and customize collection settings:
if( $debug == 2 || is_logged_in() )
{ // Allow debug info only for logged-in users OR when debug == 2:
// Enable/Disable designer mode:
$designer_mode = param( 'designer_mode', 'string' );
if( $designer_mode == 'enable' && $Session->get( 'customizer_mode_'.$blog ) )
{ // Allow to enable designer mode only together with enabled customizer mode:
$Session->set( 'designer_mode_'.$blog, 1 );
// Force to disable debug widget containers and file includes when user enables designer mode:
set_param( 'display_containers', 'hide' );
set_param( 'display_includes', 'hide' );
}
elseif( $designer_mode == 'disable' )
{
$Session->delete( 'designer_mode_'.$blog );
}
// Show/Hide the containers:
$display_containers = param( 'display_containers', 'string' );
if( $display_containers == 'show' )
{
$Session->set( 'display_containers_'.$blog, 1 );
// Force to disable designer mode when user enable to show widget containers:
$Session->delete( 'designer_mode_'.$blog );
}
elseif( $display_containers == 'hide' )
{
$Session->delete( 'display_containers_'.$blog );
}
// Show/Hide the includes:
$display_includes = param( 'display_includes', 'string' );
if( $display_includes == 'show' )
{
$Session->set( 'display_includes_'.$blog, 1 );
// Force to disable designer mode when user enable to show file includes:
$Session->delete( 'designer_mode_'.$blog );
}
elseif( $display_includes == 'hide' )
{
$Session->delete( 'display_includes_'.$blog );
}
}
if( $Session->get( 'customizer_mode_'.$Blog->ID ) && $redir != 'no' )
{ // Redirect to customize collection if such mode is enabled:
header_redirect( $Blog->get( 'customizer_url', array( 'glue' => '&' ) ) );
}
// Init $disp
$default_disp = '-'; // '-' means we have no explicit disp request yet... this may change with extraptah info or by detecting front page later
param( 'disp', '/^[a-z0-9\-_]+$/', $default_disp, true/* 'auto' does not work yet, e-g homepage /collname/?paged=2 redirect to /collname/ */ );
$disp_detail = '';
$is_front = false; // So far we have not detected that we are displaying the front page
/*
* _______________________________ Locale / Charset for the Collection _________________________________
*
*/
if( $Blog->get_setting( 'locale_source' ) == 'blog' ||
( $Blog->get_setting( 'locale_source' ) == 'user' && ! $Blog->has_locale( $current_locale ) ) )
{ // Activate main collection locale when this is defined in settings of current collection
// OR when current user/browser locale is not used for current collection:
$Debuglog->add( 'Activating blog locale: '.$Blog->get( 'locale' ), 'locale' );
locale_activate( $Blog->get( 'locale' ) );
}
$coll_locale = param( 'coll_locale', 'string', NULL, true );
if( $coll_locale !== NULL )
{ // Overriding locale from REQUEST with extra collection locale:
$Debuglog->add( 'Overriding collection locale from REQUEST: '.$coll_locale, 'locale' );
if( $Blog->has_locale( $coll_locale ) )
{ // If locale is selected for current collection:
locale_activate( $coll_locale );
}
else
{ // Wrong collection locale is requested:
$Messages->add( sprintf( T_('The requested language/locale %s is not allowed for this collection.'), '<code>'.$coll_locale.'</code>' ), 'error' );
}
}
// Re-Init charset handling, in case current_charset has changed:
if( init_charsets( $current_charset ) )
{
// Reload Blog(s) (for encoding of name, tagline etc):
$BlogCache->clear();
$Collection = $Blog = & $BlogCache->get_by_ID( $blog );
if( is_logged_in() )
{ // We also need to reload the current User with the new final charset
$UserCache = & get_UserCache();
$UserCache->clear();
$current_User = & $UserCache->get_by_ID( $current_User->ID );
}
}
/*
* _____________________________ 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 )
{
// fp> TODO: the following is kinda ok but to really work in all cases (like baseurl/a/ when coll url is baseurl/index.php/a/), we need to get the extra path right after identifying the collection
// Check and Remove blog base URI from ReqPath:
// BaseURI is the part after the domain name and it will always end with / :
$coll_baseuri = substr( $Blog->gen_baseurl(), strlen( $Blog->get_baseurl_root() ) );
$Debuglog->add( 'Collection base URI: "'.$coll_baseuri.'"', 'url_decode_part_2' );
if( $coll_baseuri_matched_in_url = preg_match( '~(^'.preg_quote( $coll_baseuri, '~' ).'|\.php[0-9]*/)(.*)$|\.php[0-9]*$~', $sanitized_ReqPath, $matches ) )
{ // Either the ReqPath starts with collection base URI (always including trailing slash) followed by some extra path info.
// - Or the ReqPath contains a .php file (which will be the case when using any slug, including old slug aliases) followed by some extra path info.
if( !empty($matches[2]) )
{
$Debuglog->add( 'Collection base URI found, WITH extra path', 'url_decode_part_2' );
$path_elements = preg_split( '~/~', $matches[2], 20, PREG_SPLIT_NO_EMPTY );
// PREVENT index.php or blog1.php etc from being considered as a slug later on.
if( isset( $path_elements[0] ) && $path_elements[0] == $pagenow )
{ // Ignore element that is the current PHP file name (ideally this URL will later be redirected to a canonical URL without any .php file in the URL)
array_shift( $path_elements );
$Debuglog->add( 'Ignoring *.php in extra path info' , 'url_decode_part_2' );
}
// pre_dump( '', $path_elements );
if( !empty($path_elements) )
{
$last_part = $path_elements[count( $path_elements )-1];
}
else
{
$last_part = '';
}
}
else
{
$Debuglog->add( 'Collection base URI found, but NO extra path', 'url_decode_part_2' );
$path_elements = array();
$last_part = '';
}
}
elseif( $Settings->get( 'always_match_slug' ) // do we (no matter what) want to redirect to correct Collection if an Item Slug was found in <b>any</b> URL?
&& strlen($sanitized_ReqPath) > strlen($basesubpath)
)
{
$Debuglog->add( 'Collection base URI not found, but we want to always match slug...', 'url_decode_part_2' );
// Find last part "/possible-slug" (possible slug):
if( preg_match( '~/([a-zA-Z0-9._\-:;]+)/?$~', $sanitized_ReqPath, $matches ) )
{
$last_part = $matches[1];
if ( $last_part != $Blog->stub // Ignore stub file (if it ends with .php it should already have been filtered out above
&& $last_part != $Blog->urlname )
{ // e-g: http://localhost/x/y/image-post instead of http://localhost/x/y/index.php/a/image-post
// e-g: http://localhost/x/y/music/ instead of http://localhost/x/y/index.php/a/fun/in-real-life/music/
$Debuglog->add( 'Possible slug: "'.$last_part.'"', 'url_decode_part_2' );
$path_elements = array( $last_part );
}
else
{
$Debuglog->add( 'Possible slug: "'.$last_part.'" but it is likely the blog ID, so not trying to match a slug', 'url_decode_part_2' );
$path_elements = array();
$last_part = '';
}
}
else
{
$Debuglog->add( 'no possible slug found', 'url_decode_part_2' );
$path_elements = array();
$last_part = '';
}
}
else
{
$Debuglog->add( 'Collection base URI not found, nothing else to do', 'url_decode_part_2' );
$path_elements = array();
$last_part = '';
}
// Do we have extra path info to decode?
if( count($path_elements) )
{
// Does the pathinfo end with a / or a ; ?
$last_char = substr( $sanitized_ReqPath, -1 );
// TAG? 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]) )
{
$tag = strip_tags(urldecode($path_elements[1]));
// # of posts per page for tag page:
if( ! $posts = $Blog->get_setting( 'tag_posts_per_page' ) )
{ // use blog default
$posts = $Blog->get_setting( 'posts_per_page' );
}
$disp = 'posts';
}
else
{
// TAG? Does the pathinfo end with a / or a ; ?
$last_len = strlen( $last_part );
$user_page_prefix = $Blog->get_setting( 'user_prefix' ).':';
$user_page_prefix_length = strlen( $user_page_prefix );
if( ( $last_char == '-' && ( ! $tags_dash_fix || $last_len != 40 ) ) // In very old b2evo version we had ITEM slugs truncated at 40 and possibly ending with `-`
|| $last_char == ':'
|| $last_char == ';' )
{ // - : or ; -> We'll consider this to be a tag page
$tag = substr( $last_part, 0, -1 );
$tag = urldecode($tag);
$tag = strip_tags($tag); // security
// pre_dump( $tag );
// # of posts per page:
if( ! $posts = $Blog->get_setting( 'tag_posts_per_page' ) )
{ // use blog default
$posts = $Blog->get_setting( 'posts_per_page' );
}
$disp = 'posts';
}
elseif( $user_page_prefix_length > 1 &&
strlen( $path_elements[0] ) > $user_page_prefix_length &&
substr( $path_elements[0], 0, $user_page_prefix_length ) == $user_page_prefix )
{ // Alias for disp=user:
$user_ID = -1; // Set -1 for case when user is not detected by login and ID
$user_request = substr( $path_elements[0], $user_page_prefix_length );
$UserCache = & get_UserCache();
if( $User = & $UserCache->get_by_login( $user_request ) ||
( is_number( $user_request ) && $User = & $UserCache->get_by_ID( $user_request, false, false ) ) )
{ // If user is detected in DB by login or ID:
$user_ID = $User->ID;
}
// Set disp to user with ID of user which was detected from request URL:
$disp = 'user';
set_param( 'user_ID', $user_ID );
}
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 to be a ref to a post: '.$last_part.' -- last char of URI: '.$last_char, 'url_decode_part_2' );
// Set a lot of defaults as if we had received a complex URL:
$m = '';
memorize_param( 'more', 'integer', 1, 1 ); // Display the extended entries' text
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
}
else
{ // Last param is a string, we'll consider this to be a post urltitle
$title = $last_part;
$Debuglog->add( 'Post slug to look for: '.$title, 'url_decode_part_2' );
}
}
else
{ // ENDING SLASH -> we are looking for a daterange OR a chapter:
$Debuglog->add( 'Last part: '.$last_part , 'url_decode_part_2' );
// echo $last_part;
if( preg_match( '|^w?[0-9]+$|', $last_part ) )
{ // Last part is a number or a "week" number:
$i=0;
$Debuglog->add( 'Last part is a number or a "week" number: '.$path_elements[$i] , 'url_decode_part_2' );
// echo $path_elements[$i];
if( isset( $path_elements[$i] ) )
{
if( preg_match( '#^\d{4}$#', $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 , 'url_decode_part_2' );
// 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' ) )
{ // use blog default
$posts = $Blog->get_setting( 'posts_per_page' );
}
if( isset( $path_elements[$i] ) && preg_match( '#^(0[1-9]|1[0-2])$#', $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 , 'url_decode_part_2' );
if( isset( $path_elements[$i] ) && preg_match( '#^(0[1-9]|[12][0-9]|3[01])$#', $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 , 'url_decode_part_2' );
}
}
elseif( isset( $path_elements[$i] ) && preg_match( '#^w(0?[0-9]|[1-4][0-9]|5[0-3])$#', $path_elements[$i] ) )
{ // We consider this a week number
$w = substr( $path_elements[$i], 1, 2 );
}
$disp = 'posts';
}
else
{ // We did not get a number/year...
$disp = '404';
$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_ChapterCache();
/**
* @var Chapter
*/
$Chapter = & $ChapterCache->get_by_urlname( $last_part, false );
if( empty( $Chapter ) )
{ // 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;
}
else
{ // We could match a chapter from the extra path:
$cat = $Chapter->ID;
// Also use the prefered posts per page for a cat
if( ! $posts = $Blog->get_setting( 'chapter_posts_per_page' ) )
{ // use blog default
$posts = $Blog->get_setting( 'posts_per_page' );
}
$disp = 'posts';
}
}
else
{ // We did not get anything we can decode...
// echo 'neither number nor cat';
$disp = '404';
$disp_detail = '404-malformed_url-bad_char';
}
}
}
}
elseif( !$coll_baseuri_matched_in_url && !empty( $path_elements))
{ // Case of calling http://baseurl/slug when main coll is set as http://baseurl/index.php and we did not elect to always match the slug.
$disp = '404';
$disp_detail = '404-unexpected-extra-path';
}
}
/*
* ____________________________ 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', '', 'auto' ); // urtitle of post to display
param( 'preview', 'integer', 0, true ); // Is this preview ?
param( 'preview_block', 'integer', 0, true ); // Should we display blocks of included content-block Items by short tags [include:] or [cblock:] ?
param( 'stats', 'integer', 0 ); // Deprecated but might still be used by spambots
/*
* ____________________________ Get specific Item if requested ____________________________
*/
if( !empty($p) || !empty($title) )
{ // We are going to display a single post
$title = rawurldecode($title);
// Make sure the single post we're requesting (still) exists:
$ItemCache = & get_ItemCache();
if( !empty($p) )
{ // Get from post ID:
$Item = & $ItemCache->get_by_ID( $p, false );
if( !empty($Item) )
{
$Debuglog->add( 'Requested Item found by $p: '.$Item->get_title(), 'url_decode_part_2' );
}
}
else
{ // Get from post title:
$orig_title = $title;
// Remove .html or .htm extension:
$title = preg_replace( '/\.(html|htm)$/', '', $title );
// Convert all special chars to -
$title = preg_replace( '/[^A-Za-z0-9_]/', '-', $title );
// Search item by title:
$Item = & $ItemCache->get_by_urltitle( $title, false, false );
if( empty( $Item ) && substr( $title, -1 ) == '-' )
{ // Try lookup by removing last invalid chars, which might have been e.g. > | "> | , | ,. | ">?!
$title = preg_replace( '/\-+$/', '', $title );
$Item = & $ItemCache->get_by_urltitle( $title, false, false );
}
if( !empty($Item) )
{
$Debuglog->add( 'Requested Item found by title: '.$Item->get_title(), 'url_decode_part_2' );
if( ! $Item->is_part_of_blog( $blog ) )
{ // We have found an Item object, but it doesn't belong to the current collection!
// Check if we want to redirect moved posts:
if( $Settings->get( 'redirect_moved_posts' ) )
{ // Set disp to 'redirect' in order to store this value in hitlog table:
$disp = 'redirect';
// Redirect to the item current permanent url:
$Debuglog->add( 'Redirecting to correct collection (through canonical URL)', 'url_decode_part_2' );
header_redirect( $Item->get_permanent_url(), 301 );
// already exited
}
$Debuglog->add( 'FORGETTING that Item now because it\'s in a different collection and we don\'t want to redirect moved posts', 'url_decode_part_2' );
unset($Item);
}
// So here we know the Item is part of the current blog/collection....
if( !empty($Item) &&
$Blog->get_setting( 'canonical_item_urls' ) &&
( $SlugCache = & get_SlugCache() ) &&
( $item_Slug = & $SlugCache->get_by_ID( $Item->get( 'canonical_slug_ID' ), false, false ) ) &&
( $item_Slug->get( 'title' ) != $title ) // If current slug is NOT canonical slug of the Item
// redundant check: && $Item->is_part_of_blog( $blog ) // If the Item has a category from current collection
)
{ // Redirect permanently to the item main/canonical permanent url in the current collection:
$Debuglog->add( 'Redirecting to correct canonical slug but stay in current collection', 'url_decode_part_2' );
$canonical_url = $Item->get_permanent_url( '', $Blog->get( 'url' ), '&', array(), $blog );
// Keep ONLY allowed params from current URL in the canonical URL by configs AND Item's switchable params:
$canonical_url = url_keep_canonicals_params( $canonical_url, '&', array_keys( $Item->get_switchable_params() ) );
header_redirect( $canonical_url, 301 );
// Exit here.
}
}
}
if( empty( $Item ) )
{ // Post doesn't exist!
// fp> TODO: ->viewing_allowed() for draft, private, protected and deprecated...
$title_fallback = false;
$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...
$Debuglog->add( 'Trying to identify a Category/Chapter...', 'url_decode_part_2' );
$ChapterCache = & get_ChapterCache();
/**
* @var Chapter
*/
$Chapter = & $ChapterCache->get_by_urlname( $title, false );
if( !empty( $Chapter ) )
{ // We could match a chapter from the extra path:
$cat = $Chapter->ID;
$title_fallback = true;
$title = NULL;
// Also use the prefered posts per page for a cat
if( ! $posts = $Blog->get_setting( 'chapter_posts_per_page' ) )
{ // use blog default
$posts = $Blog->get_setting( 'posts_per_page' );
}
}
}
if( !empty($title) )
{ // Let's try to fall back to a tag...
$Debuglog->add( 'Trying to identify a Tag...', 'url_decode_part_2' );
if( $tag_fallback )
{
$title = substr( $orig_title, 0, -1 );
}
if( $Blog->get_tag_post_count( $title ) )
{ // We could match a tag from the extra path:
$tag = $title;
$title_fallback = true;
$title = NULL;
}
}
if( ! $title_fallback )
{ // Let's try to fall back to a help slug...
$Debuglog->add( 'Trying to identify a help slug..', 'url_decode_part_2' );
$SlugCache = & get_SlugCache();
$Slug = & $SlugCache->get_by_name( $title, false, false );
if( ! empty($Slug) && $Slug->get( 'type' ) == 'help' )
{ // We could match a help slug from the extra path:
$disp = 'help';
$title_fallback = true;
$title = NULL;
}
}
if( ! $title_fallback )
{ // We were not able to fallback to anything meaningful:
$Debuglog->add( 'Could not identify anything! This is a 404!', 'url_decode_part_2' );
$disp = '404';
$disp_detail = '404-item-not-found';
$requested_404_title = $title;
}
}
}
$Debuglog->add( 'Disp detail: '.$disp_detail, 'url_decode_part_2' );
// Check if a forced skin has been requested (used by mobile skin switcher widget):
param( 'force_skin', 'string', '' );
if( ! empty( $force_skin ) )
{ // Set the forced skin from request
if( $force_skin == 'auto' )
{ // Delete the forced skin from Session to use default skin
$Session->delete( 'force_skin' );
}
else
{ // Save the forced skin in Session
$Session->set( 'force_skin', $force_skin );
}
}
else
{ // Try to get a skin from session
$force_skin = $Session->get( 'force_skin' );
}
if( ! empty( $force_skin ) )
{ // The forced skin is defined in request or in Session
$skin = $Blog->get_skin_folder( $force_skin );
}
// Check if a temporary skin has been requested (used for RSS syndication for example):
param( 'tempskin', 'string', '', true );
if( !empty( $tempskin ) )
{ // This will be handled like any other skin:
// TODO: maybe restrict that to authorized users
$skin = $tempskin;
if( empty( $disp ) || $disp == '-' )
{ // Set default disp for RSS skins
$disp = 'posts';
}
}
// Set $disp to 'posts' when filter by categories or tags or date
param( 'catsel', 'array:integer', NULL );
param( 'cat', 'string', NULL );
param( 'tag', 'string', NULL );
param( 'm', 'string', NULL );
if( empty( $Item ) &&
$disp != 'compare' && // This disp uses a filter like cat=, tag=, orderby= etc. so we should not force it to disp=post
(
! is_null( $catsel ) || // Filter by many categories
( $disp != 'edit' && $disp != 'anonpost' && ! is_null( $cat ) ) || // Filter by one category
! is_null( $tag ) || // Filter by tag
! empty( $m ) // Filter by date like '201410' (urls from ?disp=arcdir)
) )
{
$disp = 'posts';
}
unset( $catsel );
/*
* ____________________________ "Clean up" the request ____________________________
*
* Make sure that:
* 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
* This will also detect that we are on the front page (if nothing has triggered a specific $disp)
*/
if( $stats || $disp == 'stats' )
{ // This used to be a spamfest...
siteskin_init();
siteskin_include( '_410_stats_gone.main.php' ); // error
exit(0);
// EXIT.
}
elseif( !empty($preview) )
{ // Preview
memorize_param( 'disp', 'string', 'single', 'single' );
// Consider this as an admin hit!
$Hit->hit_type = 'admin';
}
elseif( $disp == '-' && !empty($Item) )
{ // We have not requested a specific disp but we have identified a specific post to be displayed
// We are going to display a single post
if( in_array( $Item->get_type_setting( 'usage' ), array( 'special', 'content-block' ) ) )
{ // Display 404 page for all "Content Blocks" and "Special" items intead of normal single page:
$disp = '404';
}
elseif( preg_match( '|[&?](download=\d+)|', $ReqURI ) )
{
$disp = 'download';
// erhsatingin> Is this the right place to increment the download count?
$link_ID = param( 'download', 'integer', false);
$LinkCache = & get_LinkCache();
if( ( $download_Link = & $LinkCache->get_by_ID( $link_ID, false, false ) ) && // Link exists in DB
( $download_File = & $download_Link->get_File() ) && // Link has a correct File object
( $download_File->exists() ) // File exists on the disk
)
{
$download_File->increment_download_count();
}
}
elseif( $Item->get_type_setting( 'usage' ) == 'page' )
{
memorize_param( 'disp', 'string', 'page', 'page' );
}
elseif( $Item->get_type_setting( 'usage' ) == 'widget-page' )
{
memorize_param( 'disp', 'string', 'widget_page', 'widget_page' );
}
else
{
memorize_param( 'disp', 'string', 'single', 'single' );
}
}
elseif( $disp == '-' || ( $disp == 'front' && $disp == $Blog->get_setting( 'front_disp' ) ) )
{ // No specific request of any kind OR
$requested_disp = $disp;
// We consider this is home front page:
memorize_param( 'disp', 'string', $Blog->get_setting( 'front_disp' ), $Blog->get_setting( 'front_disp' ) );
// Note: the above is where we MIGHT in fact set $disp = 'front';
$is_front = true; // we have detected that we are displaying the front page
if( $disp == 'single' )
{ // We must find first item from disp=posts and display it on front page:
if( $Item = & $Blog->get_first_mainlist_Item() )
{ // The item is found, Use it:
memorize_param( 'p', 'integer', $Item->ID, $Item->ID );
$c = 1; // Display comments
}
if( empty( $Item ) )
{ // If item is not found, display 404 page with below error message:
$Messages->add( sprintf( T_('Front page is set to display first post but there is nothing to display.'), $p ), 'error' );
}
}
elseif( $disp == 'page' )
{ // Specific page is displayed on front page
memorize_param( 'p', 'integer', $Blog->get_setting( 'front_post_ID' ), $Blog->get_setting( 'front_post_ID' ) );
$c = 1; // Display comments
$ItemCache = & get_ItemCache();
$Item = & $ItemCache->get_by_ID( $p, false );
if( empty( $Item ) || ! in_array( $Item->get_type_setting( 'usage' ), array ( 'page', 'widget-page' ) ) )
{ // Display error when page or widget-page Item is not found:
$Messages->add( sprintf( T_('Front page is set to display page ID=%d but it does not exist.'), $p ), 'error' );
}
elseif( $Item->get_type_setting( 'usage' ) == 'widget-page' )
{ // Switch to proper disp for Widget-Page Item in order to set correct filters on init $MainList:
memorize_param( 'disp', 'string', 'widget_page', 'widget_page' );
}
}
}
if( $disp == 'terms' )
{ // Display a page of terms & conditions:
$terms_item_ID = intval( $Settings->get( 'site_terms' ) );
if( $Settings->get( 'site_terms_enabled' ) && $terms_item_ID > 0 )
{ // Only if item ID is defined for terms page:
memorize_param( 'p', 'integer' , $terms_item_ID, $terms_item_ID );
$c = 0; // Don't display comments
$ItemCache = & get_ItemCache();
$Item = & $ItemCache->get_by_ID( $p, false );
if( is_logged_in() && $UserSettings->get( 'terms_accepted', $current_User->ID ) )
{ // Display the message if current user already accepted the terms:
$Messages->add( T_('You already accepted these terms.'), 'success' );
}
// Don't redirect to permanent url of the page:
$redir = 'no';
}
}
if( ! empty( $is_front ) )
{ // Do we need to handle the canoncial url for front page?
if( ( $Blog->get_setting( 'canonical_homepage' ) && $redir == 'yes' )
|| $Blog->get_setting( 'relcanonical_homepage' )
|| $Blog->get_setting( 'self_canonical_homepage' ) )
{ // Check if the URL was canonical:
$canonical_url = $Blog->gen_blogurl();
if( ! empty( $Item ) )
{ // Also keep front Item's switchable params in the collection canonical URL:
$keep_additional_front_canonicals_params = array_keys( $Item->get_switchable_params() );
}
else
{ // No additional canonicals params for current front page:
$keep_additional_front_canonicals_params = array();
}
// Keep ONLY allowed params from current URL in the canonical URL by configs AND additional params if they are allowed depending on front disp:
$canonical_url = url_keep_canonicals_params( $canonical_url, '&', $keep_additional_front_canonicals_params );
// Consider URL with possible params like disp=front or coll_locale=en-US as front canonical URL of the current Collection:
$current_url = preg_replace( '#[\?&]((coll_locale=[^&]+|disp='.preg_quote( $disp ).')(&|$))+#', '', $ReqURL );
if( ! is_same_url( $current_url, $canonical_url, $Blog->get_setting( 'http_protocol' ) == 'allow_both' ) )
{ // We are not on the canonical blog url:
if( $Blog->get_setting( 'canonical_homepage' ) &&
$redir == 'yes' &&
$requested_disp != 'front' ) // Do NOT redirect when current requested URL is like ?disp=front
{ // REDIRECT TO THE CANONICAL URL:
header_redirect( $canonical_url, ( empty( $display_containers ) && empty( $display_includes ) && empty( $_GET['debug'] ) ) ? 301 : 303 );
}
elseif( $Blog->get_setting( 'relcanonical_homepage' ) )
{ // Use link rel="canoncial":
add_headline( '<link rel="canonical" href="'.$canonical_url.'" />' );
}
}
elseif( $Blog->get_setting( 'self_canonical_homepage' ) )
{ // Use self-referencing rel="canonical" tag:
add_headline( '<link rel="canonical" href="'.$canonical_url.'" />' );
}
}
}
if( $disp == 'single' || $disp == 'page' || $disp == 'widget_page' )
{ // Check if the requested Item can be correctly displayed for disp 'single', 'page' and 'widget_page':
if( ! $preview && empty( $Item ) )
{ // If Item is not defined/not found in DB
// Note: The 'preview' action is the only one exception, but that is handled above in this if statement
$disp = '404';
$disp_detail = '404-item-not-found';
}
elseif( ! $preview && $Item->status == 'deprecated' )
{ // If the requested Item is deprecated
$disp = '404';
$disp_detail = '404-item-deprecated';
}
elseif( ! $preview && $Item->status == 'redirected' )
{ // $redir=no here allows to force a 'single post' URL for commenting
// Redirect to the URL specified in the post:
$Debuglog->add( 'Redirecting to post URL ['.$Item->url.'].' );
header_redirect( $Item->url, true, true );
}
elseif( ! $preview && ! in_array( $Item->status, get_inskin_statuses( $Blog->ID, 'post' ) ) )
{ // If the requested Item is not allowed to be displayed on front-office
$disp = '404';
$disp_detail = '404-item-disallowed-for-frontoffice';
}
elseif( ! is_logged_in() && in_array( $Item->status, array( 'community', 'protected' ) ) )
{ // If the requested Item is allowed only for community or members:
$login_Blog = & get_setting_Blog( 'login_blog_ID' );
if( $login_Blog && $login_Blog->ID != $Blog->ID )
{ // If current collection is not used for login actions,
// Redirect to login form on "access_requires_login.main.php":
header_redirect( get_login_url( '403 item requires login', NULL, false, NULL, 'content_requires_loginurl' ), 302 );
// will have exited
}
else
{ // Current collection is used for login actions
// Don't redirect, just display a login form of the collection:
$disp = 'content_requires_login';
$disp_detail = '403-item-requires-login';
// Set redirect_to param to current url in order to display a requested page after login action:
global $ReqURI;
param( 'redirect_to', 'url', $ReqURI );
}
}
elseif( ! $preview && ! $Item->can_be_displayed() )
{ // If current User has no permission to view the requested Item
$disp = '403';
$disp_detail = '403-item-disallowed-for-user';
}
elseif( ! $preview )
{ // Check single/page view:
switch( $Item->get( 'single_view' ) )
{
case '404':
// Force to 404 page:
$disp = '404';
$disp_detail = '404-item-disallowed-single-view';
break;
case 'redirected':
// Try to force a redirect:
if( empty( $Item->url ) )
{ // Display 404 page if no url is provided to redirect:
$disp = '404';
$disp_detail = '404-item-missing-redirect-url';
}
else
{ // Redirect only with filled URL:
$Debuglog->add( 'Redirecting to post URL ['.$Item->url.'] because of single/page view.' );
header_redirect( $Item->url, true, true );
}
break;
}
}
}
param( 'user_ID', 'integer', NULL );
if( ( $disp == 'user' ) && isset( $user_ID ) && isset( $current_User ) && ( $user_ID != $current_User->ID ) && ( $Settings->get( 'enable_visit_tracking') == 1 ) )
{ // add or increment to user profile visit
add_user_profile_visit( $user_ID, $current_User->ID );
}
elseif( ( $disp == 'visits' ) && isset( $user_ID ) && isset( $current_User ) && ( $user_ID == $current_User->ID ) && ( $Settings->get( 'enable_visit_tracking') == 1 ) )
{
reset_user_profile_view_ts( $user_ID );
}
// Check if terms & conditions should be accepted by current user:
if( is_logged_in() && // Only for logged in users
! in_array( $disp, array( 'terms', 'help', 'msgform', 'activateinfo' ) ) && // Allow these pages
$Settings->get( 'site_terms_enabled' ) && // Terms must be enabled
! $UserSettings->get( 'terms_accepted', $current_User->ID ) ) // If it was not accepted yet
{ // Current user didn't accept the terms yet:
// Get ID of page with terms & conditions from global settings:
$terms_page_ID = intval( $Settings->get( 'site_terms' ) );
$ItemCache = & get_ItemCache();
if( $terms_page_ID &&
$terms_Item = & $ItemCache->get_by_ID( $terms_page_ID, false, false ) &&
$terms_item_Blog = & $terms_Item->get_Blog() )
{ // Redirect to view page with terms & conditions if it is defined correctly in settings:
$Messages->add( T_('You need to accept the following before you can enter this site.'), 'note' );
header_redirect( $terms_item_Blog->get( 'termsurl', array(
'url_suffix' => 'redirect_to='.rawurlencode( $ReqURI ),
'glue' => '&',
) ), 303 );
// EXIT HERE
}
}
/*
* ______________________ DETERMINE WHICH SKIN TO USE FOR DISPLAY _______________________
*/
if( isset( $skin ) )
{ // A skin has been requested by folder_name (url or stub):
// Check validity of requested skin name:
if( preg_match( '~([^-A-Za-z0-9._]|\.\.)~', $skin ) )
{
debug_die( 'The requested skin name is invalid.' );
}
$SkinCache = & get_SkinCache();
$Skin = & $SkinCache->new_obj( NULL, $skin );
if( $Skin->type == 'feed' )
{ // Check if we actually allow the display of the feed; last chance to revert to 404 displayed in default skin.
// Note: Skins with the type "feed" can always be accessed, even when they're not installed.
if( ( $disp == 'posts' && $Blog->get_setting('feed_content') == 'none' ) ||
( $disp == 'comments' && $Blog->get_setting('comment_feed_content') == 'none' ) )
{ // We don't want to provide feeds; revert to 404!
unset( $skin );
unset( $Skin );
$disp = '404';
$disp_detail = '404-feeds-disabled';
}
}
elseif( $Skin->type == 'sitemap' )
{ // Check if we actually allow the display of sitemaps.
// Note: Skins with the type "sitemap" can always be accessed, even when they're not installed.
if( ! $Blog->get_setting('enable_sitemaps') )
{ // We don't want to show this sitemap, revert to error 404:
unset( $skin );
unset( $Skin );
$disp = '404';
$disp_detail = '404-sitemaps-disabled';
}
}
elseif( skin_exists( $skin ) && ! skin_installed( $skin ) )
{ // The requested skin is not a feed skin and exists in the file system, but isn't installed:
debug_die( sprintf( T_( 'The skin [%s] is not installed on this system.' ), htmlspecialchars( $skin ) ) );
}
elseif( ! empty( $tempskin ) )
{ // By definition, we want to see the temporary skin (if we don't use feedburner... )
$redir = 'no';
}
}
$blog_skin_ID = $Blog->get_skin_ID();
if( !isset( $skin ) && !empty( $blog_skin_ID ) ) // Note: if $skin is set to '', then we want to do a "no skin" display
{ // Use default skin from the database
$SkinCache = & get_SkinCache();
$Skin = & $SkinCache->get_by_ID( $blog_skin_ID );
$skin = $Skin->folder;
}
// Because a lot of bloggers will delete skins, we have to make this fool proof with extra checking:
if( !empty( $skin ) && !skin_exists( $skin ) )
{ // We want to use a skin, but it doesn't exist!
$err_msg = sprintf( T_('The skin [%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.'),
htmlspecialchars($skin),
$Blog->dget('shortname'),
'href="'.$admin_url.'?ctrl=coll_settings&tab=skin&blog='.$Blog->ID.'"' );
debug_die( $err_msg );
}
/*
* _______________ Name request/transaction for performance logging ________________
*
* A this point, we know what the request/transaction is about and we can give it
* a meaning ful name.
*/
$request_transaction_name = $Blog->shortname;
if( !empty($disp_detail) )
{
$request_transaction_name .= ':'.$disp_detail;
}
elseif( !empty($disp) )
{
$request_transaction_name .= ':'.$disp;
}
$Debuglog->add( 'Transaction name: '.$request_transaction_name, 'request' );
// Let's name the transaction for proper APM reporting:
apm_name_transaction( $request_transaction_name );
$Timer->pause( '_BLOG_MAIN.inc');
// LOG with APM:
$Timer->log_duration( '_BLOG_MAIN.inc' );
// Init global vars which may be required for any skin
skin_init_global_vars();
// Check if current user has access to this blog
$Blog->check_access();
/*
* _______________________________ READY TO DISPLAY _______________________________
*
* At this point $skin holds the name of the skin we want to use, or '' for no skin!
*/
// Trigger plugin event:
// fp> TODO: please doc with example of what this can be used for
$Plugins->trigger_event( 'BeforeBlogDisplay', array( 'skin' => $skin ) );
if( !empty( $skin ) )
{ // We want to display with a skin now:
$Timer->resume( 'SKIN DISPLAY' );
$Debuglog->add( 'Selected skin: '.$skin, 'skins' );
// Instantiate PageCache:
$Timer->resume( 'PageCache' );
load_class( '_core/model/_pagecache.class.php', 'PageCache' );
$PageCache = new PageCache( $Blog );
// Check for cached content & Start caching if needed
// Note: there are some redirects inside the skins 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:
$Timer->pause( 'PageCache' );
if( $skin_provided_by_plugin = skin_provided_by_plugin( $skin ) )
{
$tmp_params = array( 'skin' => $skin );
$Plugins->call_method( $skin_provided_by_plugin, 'DisplaySkin', $tmp_params );
}
else
{
// Path for the current skin:
$ads_current_skin_path = $skins_path.$skin.'/';
$disp_handlers = array(
'403' => '403_forbidden.main.php',
'404' => '404_not_found.main.php',
'access_denied' => 'access_denied.main.php',
'access_requires_login' => 'access_requires_login.main.php',
'content_requires_login'=> 'content_requires_login.main.php',
'activateinfo' => 'activateinfo.main.php',
'anonpost' => 'anonpost.main.php',
'arcdir' => 'arcdir.main.php',
'catdir' => 'catdir.main.php',
'closeaccount' => 'closeaccount.main.php',
'comments' => 'comments.main.php',
'contacts' => 'contacts.main.php',
'download' => 'download.main.php',
'edit' => 'edit.main.php',
'proposechange' => 'proposechange.main.php',
'edit_comment' => 'edit_comment.main.php',
'feedback-popup' => 'feedback_popup.main.php',
'flagged' => 'flagged.main.php',
'mustread' => 'mustread.main.php',
'front' => 'front.main.php',
'help' => 'help.main.php',
'login' => 'login.main.php',
'lostpassword' => 'lostpassword.main.php',
'mediaidx' => 'mediaidx.main.php',
'messages' => 'messages.main.php',
'module_form' => 'module_form.main.php',
'msgform' => 'msgform.main.php',
'page' => 'page.main.php',
'widget_page' => 'widget_page.main.php',
'postidx' => 'postidx.main.php',
'posts' => 'posts.main.php',
'profile' => 'profile.main.php',
'avatar' => 'avatar.main.php',
'pwdchange' => 'pwdchange.main.php',
'userprefs' => 'userprefs.main.php',
'subs' => 'subs.main.php',
'visits' => 'visits.main.php',
'register' => 'register.main.php',
'register_finish' => 'register_finish.main.php',
'search' => 'search.main.php',
'single' => 'single.main.php',
'sitemap' => 'sitemap.main.php',
'tags' => 'tags.main.php',
'terms' => 'terms.main.php',
'threads' => 'threads.main.php',
'contacts' => 'contacts.main.php',
'user' => 'user.main.php',
'useritems' => 'useritems.main.php',
'usercomments' => 'usercomments.main.php',
'users' => 'users.main.php',
'compare' => 'compare.main.php',
// All others will default to index.main.php
);
// Handle custom templates defined by the Item Type:
if( ! empty( $disp ) && ( $disp == 'single' || $disp == 'page' || $disp == 'widget_page' ) &&
! empty( $Item ) && ( $ItemType = & $Item->get_ItemType() ) && $ItemType->get( 'template_name' ) != '' )
{ // Get template name for the current Item if it is defined by its Item Type:
$disp_handler_custom = $ItemType->get( 'template_name' ).'.main.php';
if( $Skin->get_api_version() == 7 && file_exists( $disp_handler = $ads_current_skin_path.$Blog->get( 'type' ).'/'.$disp_handler_custom ) )
{ // Custom template is found in skin folder for current collection kind:
$disp_handler_custom_found = true;
$Debuglog->add('blog_main: include '.rel_path_to_base( $disp_handler ).' (custom for item type and collection kind)', 'skins' );
}
elseif( file_exists( $disp_handler = $ads_current_skin_path.$disp_handler_custom ) )
{ // Custom template is found in skin folder:
$disp_handler_custom_found = true;
$Debuglog->add('blog_main: include '.rel_path_to_base( $disp_handler ).' (custom for item type)', 'skins' );
}
else
{ // Custom template not found:
$disp_handler = NULL;
}
}
if( empty( $disp_handler ) )
{ // Set $disp_handler only if it is not defined above:
if( ! empty( $disp_handlers[ $disp ] ) )
{
if( $Skin->get_api_version() == 7 && file_exists( $disp_handler = $ads_current_skin_path.$Blog->get( 'type' ).'/'.$disp_handlers[ $disp ] ) )
{ // The current skin has a customized page handler for this disp and current collection kind:
$Debuglog->add('blog_main: include '.rel_path_to_base( $disp_handler ).' (custom to this theme and collection kind)', 'skins' );
}
elseif( file_exists( $disp_handler = $ads_current_skin_path.$disp_handlers[ $disp ] ) )
{ // The current skin has a customized page handler for this disp:
$Debuglog->add('blog_main: include '.rel_path_to_base( $disp_handler ).' (custom to this skin)', 'skins' );
}
elseif( $Skin->get_api_version() == 7 && file_exists( $disp_handler = $ads_current_skin_path.$Blog->get( 'type' ).'/index.main.php' ) )
{ // Fallback to the default "index" handler from the current skin dir for current collection kind:
$Debuglog->add('blog_main: include '.rel_path_to_base( $disp_handler ).' (default handler for collection kind)', 'skins' );
}
else
{ // Fallback to the default "index" handler from the current skin dir:
$disp_handler = $ads_current_skin_path.'index.main.php';
$Debuglog->add('blog_main: include '.rel_path_to_base( $disp_handler ).' (default handler)', 'skins' );
}
}
else
{ // Use the default handler from the skins dir:
if( $Skin->get_api_version() == 7 && file_exists( $disp_handler = $ads_current_skin_path.$Blog->get( 'type' ).'/index.main.php' ) )
{ // For current collection kind:
$Debuglog->add('blog_main: include '.rel_path_to_base( $disp_handler ).' (default handler for collection kind)', 'skins' );
}
else
{ // For all other kinds:
$disp_handler = $ads_current_skin_path.'index.main.php';
$Debuglog->add('blog_main: include '.rel_path_to_base( $disp_handler ).' (default index handler)', 'skins' );
}
}
}
// CALL THE MAIN TEMPLATE NOW:
require $disp_handler;
}
// Save collected cached data if needed:
$PageCache->end_collect();
}
if( $Timer->get_state( 'PageCache' ) == 'running' )
{ // Pause only when the page cache timer was not stoped above:
$Timer->pause( 'PageCache' );
}
$Timer->pause( 'SKIN DISPLAY' );
// LOG with APM:
$Timer->log_duration( 'SKIN DISPLAY' );
// We probably don't want to return to the caller if we have displayed a skin...
// That is useful if the caller implements a custom display but we still use skins for RSS/ Atom etc..
exit(0);
}
else
{ // We don't use a skin. Hopefully the caller will do some displaying.
// Set a few vars with default values, just in case...
$ads_current_skin_path = $htsrv_path;
// We'll just return to the caller now... (if we have not used a skin, the caller should do the display after this)
}
?>