3
3
* This file initializes everything BUT the blog!
5
* It is useful when you want to do very customized templates!
6
* It is also called by more complete initializers.
8
* This file is part of Quam Plures - {@link http://quamplures.net/}
9
* See also {@link https://launchpad.net/quam-plures}.
11
* @copyright (c) 2009 - 2011 by the Quam Plures developers - {@link http://quamplures.net/}
12
* @copyright (c)2003-2009 by Francois PLANQUE - {@link http://fplanque.net/}
13
* Parts of this file are copyright (c)2004-2006 by Daniel HAHLER - {@link http://thequod.de/contact}.
14
* Parts of this file are copyright (c)2005-2006 by PROGIDISTRI - {@link http://progidistri.com/}.
16
* {@internal License choice
17
* - If you have received this file as part of a package, please find the license.txt file in
18
* the same folder or the closest folder above for complete license terms.
19
* - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
20
* then you must choose one of the following licenses before using the file:
21
* - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
22
* - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
25
* {@internal Open Source relicensing agreement:
26
* Daniel HAHLER grants Francois PLANQUE the right to license
27
* Daniel HAHLER's contributions to this file and the b2evolution project
28
* under any OSI approved OSS license (http://www.opensource.org/licenses/).
30
* PROGIDISTRI S.A.S. grants Francois PLANQUE the right to license
31
* PROGIDISTRI S.A.S.'s contributions to this file and the b2evolution project
32
* under any OSI approved OSS license (http://www.opensource.org/licenses/).
34
* Matt FOLLETT grants Francois PLANQUE the right to license
35
* Matt FOLLETT's contributions to this file and the b2evolution project
36
* under any OSI approved OSS license (http://www.opensource.org/licenses/).
39
* {@internal Below is a list of authors who have contributed to design/coding of this file: }}
40
* @author fplanque: Francois PLANQUE
41
* @author blueyed: Daniel HAHLER
42
* @author mfollett: Matt FOLLETT.
43
* @author mbruneau: Marc BRUNEAU / PROGIDISTRI
5
* It is useful when you want to do very customized templates! It is also
6
* called by more complete initializers.
8
* @author {@link http://wonderwinds.com/ Ed Bennett}
9
* @author {@link http://fplanque.net/ Francois PLANQUE}
10
* @author {@link http://daniel.hahler.de/ Daniel HAHLER}
11
* @author {@link http://www.mfollett.com/ Matt FOLLETT}
12
* @author {@link http://progidistri.com/ PROGIDISTRI}
13
* @copyright (c) 2009 by {@link http://quamplures.net/ the Quam Plures project}
14
* @license http://www.gnu.org/licenses/gpl.txt GNU General Public License v3
47
if( !defined('QP_CONFIG_LOADED') ) die( 'Please, do not access this page directly.' );
17
if(!defined('QP_CONFIG_LOADED')) die('fail');
49
19
if( $maintenance_mode )
163
96
require_once dirname(__FILE__).'/_vars.inc.php';
166
if( !$app_config_is_done )
167
{ // base config is not done!
98
if( ! $app_config_is_done )
100
// base config is not done!
168
101
$error_message = 'Base configuration is not done! (see /qp_config/_main_config.php)';
170
elseif( !isset( $locales[$default_locale] ) )
103
elseif( ! isset( $locales[$default_locale] ) )
172
105
$error_message = 'The default locale '.var_export( $default_locale, true ).' does not exist! (see /qp_config/_locales.php)';
174
107
if( isset( $error_message ) )
176
112
require dirname(__FILE__).'/../qp_view_admin/conf_error.main.php';
183
118
require_once dirname(__FILE__).'/_connect_db.inc.php';
187
* Load settings class
189
load_class('settings/model/_generalsettings.class.php');
190
load_class('users/model/_usersettings.class.php');
192
121
* Interface to general settings
194
123
* Keep this below the creation of the {@link $DB DB object}, because it checks for the
195
124
* correct db_version and catches "table does not exist" errors, providing a link to the
196
125
* install script.
198
126
* @global GeneralSettings $Settings
200
128
$Settings = new GeneralSettings();
202
131
* Interface to user settings
204
132
* @global UserSettings $UserSettings
206
134
$UserSettings = new UserSettings();
210
137
* Absolute Unix timestamp for server
211
* @global int $servertimenow
138
* @global integer $servertimenow
213
140
$servertimenow = time();
215
$time_difference = $Settings->get('time_difference');
142
$time_difference = $Settings->get( 'time_difference' );
218
145
* Corrected Unix timestamp to match server timezone
219
* @global int $localtimenow
146
* @global integer $localtimenow
221
148
$localtimenow = $servertimenow + $time_difference;
227
load_class('sessions/model/_hit.class.php');
228
151
// fp> The following constructor requires these right now:
229
load_funcs('_core/_param.funcs.php');
230
load_funcs('_core/_url.funcs.php');
235
* We need to do this as early as possible in order to set DB connection charset below
236
* fp> that does not explain why it needs to be here!! Why do we need to set the Db charset HERE? BEFORE WHAT?
238
* sam2kb> ideally we should set the right DB charset at the time when we connect to the database. The reason is until we do it all data pulled out from DB is in wrong encoding. I put the code here because it depends on _param.funcs, so if move the _param.funcs higher we can also move this code right under _connect_db
239
* See also http://forums.b2evolution.net//viewtopic.php?p=95100
242
$Debuglog->add( 'default_locale from conf: '.$default_locale, 'locale' );
152
load_funcs( '_core/_param.funcs.php' );
153
load_funcs( '_core/_url.funcs.php' );
156
// We need to do this as early as possible in order to set DB connection charset below
157
// fp> that does not explain why it needs to be here!! Why do we need to set the Db
158
// charset HERE? BEFORE WHAT? sam2kb> ideally we should set the right DB charset at
159
// the time when we connect to the database. The reason is until we do it all data
160
// pulled out from DB is in wrong encoding. I put the code here because it depends on
161
// _param.funcs, so if move the _param.funcs higher we can also move this code right
244
163
locale_overwritefromDB();
245
$Debuglog->add( 'default_locale from DB: '.$default_locale, 'locale' );
247
164
$default_locale = locale_from_httpaccept(); // set default locale by autodetect
248
$Debuglog->add( 'default_locale from HTTP_ACCEPT: '.$default_locale, 'locale' );
250
if( ($locale_from_get = param( 'locale', 'string', NULL, true )) )
166
if( ( $locale_from_get = param( 'locale', 'string', NULL, true ) ) )
252
168
if( $locale_from_get != $default_locale )
254
170
if( isset( $locales[$locale_from_get] ) )
256
172
$default_locale = $locale_from_get;
257
$Debuglog->add('Overriding locale from REQUEST: '.$default_locale, 'locale');
261
$Debuglog->add('$locale_from_get ('.$locale_from_get.') is not set. Available locales: '.implode(', ', array_keys($locales)), 'locale');
262
176
$locale_from_get = false;
267
$Debuglog->add('$locale_from_get == $default_locale ('.$locale_from_get.').', 'locale');
274
* Activate default locale:
182
// Activate default locale
276
183
locale_activate( $default_locale );
278
// Set encoding for MySQL connection:
185
// Set encoding for MySQL connection
279
186
$DB->set_connection_charset( $current_charset );
283
189
* @global Hit The Hit object
285
191
$Hit = new Hit(); // This may INSERT a basedomain and a useragent but NOT the HIT itself!
291
load_class('sessions/model/_session.class.php');
293
* The Session object.
294
196
* It has to be instantiated before the "SessionLoaded" hook.
197
* @todo (0000) This needs the same "SET NAMES" MySQL-setup as with Session::dbsave() -
198
* see the "TODO" with unserialize() in Session::Session()
295
199
* @global Session
296
* @todo dh> This needs the same "SET NAMES" MySQL-setup as with Session::dbsave() - see the "TODO" with unserialize() in Session::Session()
297
* @todo dh> makes no sense in CLI mode (no cookie); Add isset() checks to calls on the $Session object, e.g. below?
298
* fp> We might want to use a special session for CLI. And for cron jobs through http as well.
300
201
$Session = new Session(); // IF this can't pull asesion from the DB it will always INSERT a new one!
305
206
register_shutdown_function( 'shutdown' );
309
* @global AbstractSettings
311
$global_Cache = new AbstractSettings( 'T_global__cache', array( 'cach_name' ), 'cach_cache', 0 /* load all */ );
316
* This is done quite early here to give an early hook ("SessionLoaded") to plugins (though it might also be moved just after $DB init when there is reason for a hook there).
317
* The {@link dnsbl_antispam_plugin} is an example that uses this to check the user's IP against a list of DNS blacklists.
319
load_class('plugins/model/_plugins.class.php');
321
209
* @global Plugins The Plugin management object
323
211
$Plugins = new Plugins();
326
// NOTE: it might be faster (though more bandwidth intensive) to spit cached pages (CachePageContent event) than to look into blocking the request (SessionLoaded event).
213
// NOTE: it might be faster (though more bandwidth intensive) to spit cached pages
214
// (CachePageContent event) than to look into blocking the request (SessionLoaded event).
327
215
$Plugins->trigger_event( 'SessionLoaded' );
330
217
// Trigger a page content caching plugin. This would either return the
331
218
// cached content here or start output buffering.
332
219
if( $Session->get( 'core.no_CachePageContent' ) )
333
{ // The event is disabled for this request:
221
// The event is disabled for this request
334
222
$Session->delete('core.no_CachePageContent');
335
$Debuglog->add( 'Skipping CachePageContent event, because of core.no_CachePageContent setting.', 'plugins' );
337
224
elseif( ( $get_return = $Plugins->trigger_event_first_true( 'CachePageContent' ) ) // Plugin responded to the event
338
&& ( isset($get_return['data']) ) ) // cached content returned
225
&& ( isset($get_return['data']) ) ) // cached content returned
340
227
echo $get_return['data'];
341
228
// Note: we should not use debug_info() here, because the plugin has probably sent a Content-Length header.
346
// TODO: we need an event hook here for the transport_optimizer_plugin, which must get called, AFTER another plugin might have started an output buffer for caching already.
347
// Plugin priority is no option, because CachePageContent is a trigger_event_first_true event, for obvious reasons.
349
// This must not be exactly here, but before any output.
355
$Timer->resume('_main.inc:requires');
356
load_class('_core/model/dataobjects/_dataobjectcache.class.php');
357
load_class('generic/model/_genericelement.class.php');
358
load_class('generic/model/_genericcache.class.php');
359
load_class('collections/model/_blog.class.php');
360
load_funcs('collections/model/_blog.funcs.php');
361
load_funcs('collections/model/_category.funcs.php');
362
load_funcs('items/model/_item.funcs.php');
363
load_funcs('users/model/_user.funcs.php');
364
load_funcs('_core/_template.funcs.php');
365
load_class('files/model/_file.class.php');
366
load_class('files/model/_filetype.class.php');
367
load_class('files/model/_filetypecache.class.php');
368
load_class('items/model/_itemtype.class.php');
369
load_class('items/model/_link.class.php');
370
load_funcs('comments/model/_comment.funcs.php');
371
load_funcs('items/model/_item.funcs.php');
372
load_class('comments/model/_commentlist.class.php');
373
load_funcs('_core/ui/forms/_form.funcs.php');
374
load_class('_core/ui/forms/_form.class.php');
375
load_class('items/model/_itemquery.class.php');
233
$Timer->resume( '_main.inc:requires' );
234
load_funcs( 'blogs/model/_blog.funcs.php' );
235
load_funcs( 'categories/model/_category.funcs.php' );
236
load_funcs( 'items/model/_item.funcs.php' );
237
load_funcs( 'users/model/_user.funcs.php' );
238
load_funcs( '_core/_template.funcs.php' );
239
load_funcs( 'comments/model/_comment.funcs.php' );
240
load_funcs( 'items/model/_item.funcs.php' );
241
load_funcs( '_core/ui/forms/_form.funcs.php' );
376
242
$Timer->pause( '_main.inc:requires' );
380
* Login procedure: {{{
381
* TODO: dh> the meat of this login procedure should be moved to an extra file,
382
* so that if a "logged in"-session exists (in most cases) it does not
383
* trigger parsing the meat of this code.
384
* fp> ming you, most hits will be on the font end and will not be loggedin sessions
385
* However, I agree that the login stuff should only be included when the user is actually attempting to log in.
387
if( !isset($login_required) )
244
if( ! isset( $login_required ) )
389
246
$login_required = false;
395
251
$pass_md5 = NULL;
397
if( isset($_POST['login'] ) && isset($_POST['pwd'] ) )
398
{ // Trying to log in with a POST
253
if( isset( $_POST['login'] ) && isset( $_POST['pwd'] ) )
255
// Trying to log in with a POST
399
256
$login = $_POST['login'];
400
257
$pass = $_POST['pwd'];
401
unset($_POST['pwd']); // password will be hashed below
258
unset( $_POST['pwd'] ); // password will be hashed below
403
elseif( isset($_GET['login'] ) )
404
{ // Trying to log in with a GET; we might only provide a user here.
260
elseif( isset( $_GET['login'] ) )
262
// Trying to log in with a GET; we might only provide a user here.
405
263
$login = $_GET['login'];
406
264
$pass = isset($_GET['pwd']) ? $_GET['pwd'] : '';
407
265
unset($_GET['pwd']); // password will be hashed below
410
$Debuglog->add( 'login: '.var_export($login, true), 'login' );
411
$Debuglog->add( 'pass: '.( empty($pass) ? '' : 'not' ).' empty', 'login' );
413
// either 'login' (normal) or 'redirect_to_backoffice' may be set here. This also helps to display the login form again, if either login or pass were empty.
268
// either 'login' (normal) or 'redirect_to_backoffice' may be set here.
269
// This also helps to display the login form again, if either login or pass were empty.
414
270
$login_action = param_arrayindex( 'login_action' );
416
272
$UserCache = & get_Cache( 'UserCache' );
418
if( ! empty($login_action) || (! empty($login) && ! empty($pass)) )
419
{ // User is trying to login right now
420
$Debuglog->add( 'User is trying to log in.', 'login' );
274
if( ! empty( $login_action ) || (! empty( $login ) && ! empty( $pass ) ) )
276
// User is trying to login right now
422
277
header_nocache();
424
278
// Note: login and password cannot include '<' !
425
$login = strtolower(strip_tags(remove_magic_quotes($login)));
426
$pass = strip_tags(remove_magic_quotes($pass));
279
$login = strtolower( strip_tags( remove_magic_quotes( $login ) ) );
280
$pass = strip_tags( remove_magic_quotes( $pass ) );
427
281
$pass_md5 = md5( $pass );
431
* Handle javascript-hashed password:
432
* If possible, the login form will hash the entered password with a salt that changes everytime.
283
// Handle javascript-hashed password: If possible, the login form will hash the
284
// entered password with a salt that changes everytime.
434
285
param('pwd_salt', 'string', ''); // just for comparison with the one from Session
435
$pwd_salt_sess = $Session->get('core.pwd_salt');
437
// $Debuglog->add( 'salt: '.var_export($pwd_salt, true).', session salt: '.var_export($pwd_salt_sess, true) );
439
$transmit_hashed_password = (bool)$Settings->get('js_passwd_hashing') && !(bool)$Plugins->trigger_event_first_true('LoginAttemptNeedsRawPassword');
286
$pwd_salt_sess = $Session->get( 'core.pwd_salt' );
288
$transmit_hashed_password = (bool)$Settings->get( 'js_passwd_hashing' )
289
&& !(bool)$Plugins->trigger_event_first_true('LoginAttemptNeedsRawPassword');
440
290
if( $transmit_hashed_password )
442
292
param( 'pwd_hashed', 'string', '' );
445
{ // at least one plugin requests the password un-hashed:
296
// at least one plugin requests the password un-hashed
446
297
$pwd_hashed = '';
449
// $Debuglog->add( 'pwd_hashed: '.var_export($pwd_hashed, true).', pass: '.var_export($pass, true) );
451
300
$pass_ok = false;
452
// Trigger Plugin event, which could create the user, according to another database:
301
// Trigger Plugin event, which could create the user, according to another database
453
302
if( $Plugins->trigger_event( 'LoginAttempt', array(
454
303
'login' => & $login,
455
304
'pass' => & $pass,
457
306
'pass_salt' => & $pwd_salt_sess,
458
307
'pass_hashed' => & $pwd_hashed,
459
308
'pass_ok' => & $pass_ok ) ) )
460
{ // clear the UserCache, if a plugin has been called - it may have changed user(s)
310
// clear the UserCache, if a plugin has been called - it may have changed user(s)
461
311
$UserCache->clear();
464
314
if( $Messages->count('login_error') )
465
{ // A plugin has thrown a login error..
466
// Do nothing, the error will get displayed in the login form..
468
// TODO: dh> make sure that the user gets logged out?! (a Plugin might have logged him in and another one thrown an error)
316
// A plugin has thrown a login error; do nothing, the error will get displayed in the login form.
317
// @todo (0000) dh> make sure that the user gets logged out?! (a
318
// Plugin might have logged him in and another one thrown an error)
471
{ // Check login and password
322
// Check login and password
473
323
// Make sure that we can load the user:
474
$User = & $UserCache->get_by_login($login);
324
$User = & $UserCache->get_by_login( $login );
476
326
if( $User && ! $pass_ok )
477
{ // check the password, if no plugin has said "it's ok":
478
if( ! empty($pwd_hashed) )
479
{ // password hashed by JavaScript:
481
$Debuglog->add( 'Hashed password available.', 'login' );
483
if( empty($pwd_salt_sess) )
484
{ // no salt stored in session: either cookie problem or the user had already tried logging in (from another window for example)
485
$Debuglog->add( 'Empty salt_sess!', 'login' );
486
if( ($pos = strpos( $pass, '_hashed_' ) ) && substr($pass, $pos+8) == $Session->ID )
487
{ // session ID matches, no cookie problem
328
// check the password, if no plugin has said "it's ok"
329
if( ! empty( $pwd_hashed ) )
331
// password hashed by JavaScript
332
if( empty( $pwd_salt_sess ) )
334
// no salt stored in session: either cookie problem or the user had already tried logging in (from another window for example)
335
if( ( $pos = strpos( $pass, '_hashed_' ) ) && substr( $pass, $pos+8 ) == $Session->ID )
337
// session ID matches, no cookie problem
488
338
$Messages->add( T_('The login window has expired. Please try again.'), 'login_error' );
489
$Debuglog->add( 'Session ID matches.', 'login' );
492
{ // more general error:
342
// more general error:
493
343
$Messages->add( T_('Either you have not enabled cookies or this login window has expired.'), 'login_error' );
494
$Debuglog->add( 'Session ID does not match.', 'login' );
497
346
elseif( $pwd_salt != $pwd_salt_sess )
498
{ // submitted salt differs from the one stored in the session
348
// submitted salt differs from the one stored in the session
499
349
$Messages->add( T_('The login window has expired. Please try again.'), 'login_error' );
500
$Debuglog->add( 'Submitted salt and salt from Session do not match.', 'login' );
503
{ // compare the password, using the salt stored in the Session:
504
#pre_dump( sha1($User->pass.$pwd_salt), $pwd_hashed );
505
$pass_ok = sha1($User->pass.$pwd_salt) == $pwd_hashed;
353
// compare the password, using the salt stored in the Session:
354
$pass_ok = sha1( $User->pass.$pwd_salt ) == $pwd_hashed;
506
355
$Session->delete('core.pwd_salt');
507
$Debuglog->add( 'Compared hashed passwords. Result: '.(int)$pass_ok, 'login' );
512
$pass_ok = $User->check_password( $pass_md5, true );
513
$Debuglog->add( 'Compared raw passwords. Result: '.(int)$pass_ok, 'login' );
360
$pass_ok = ( $User->pass == $pass_md5 );
519
{ // Login succeeded, set cookies
520
$Debuglog->add( 'User successfully logged in with username and password...', 'login');
367
// Login succeeded, set cookies
521
368
// set the user from the login that succeeded
522
$current_User = & $UserCache->get_by_login($login);
369
$current_User = & $UserCache->get_by_login( $login );
523
370
// save the user for later hits
524
371
$Session->set_User( $current_User );
526
373
elseif( ! $Messages->count('login_error') )
527
{ // if there's no login_error message yet, add the default one:
375
// if there's no login_error message yet, add the default one:
528
376
// This will cause the login screen to "popup" (again)
529
377
$Messages->add( T_('Wrong login/password.'), 'login_error' );
533
381
elseif( $Session->has_User() /* logged in */
534
382
&& /* No login param given or the same as current user: */
535
( empty($login) || ( ( $tmp_User = & $UserCache->get_by_ID($Session->user_ID) ) && $login == $tmp_User->login ) ) )
383
( empty( $login ) || ( ( $tmp_User = & $UserCache->get_by_ID( $Session->user_ID ) ) && $login == $tmp_User->login ) ) )
536
384
{ /* if the session has a user assigned to it:
537
385
* User was not trying to log in, but he was already logged in:
539
387
// get the user ID from the session and set up the user again
540
388
$current_User = & $UserCache->get_by_ID( $Session->user_ID );
542
$Debuglog->add( 'Was already logged in... ['.$current_User->get('login').']', 'login' );
545
{ // The Session has no user or $login is given (and differs from current user), allow alternate authentication through Plugin:
546
if( ($event_return = $Plugins->trigger_event_first_true( 'AlternateAuthentication' ))
547
&& $Session->has_User() # the plugin should have attached the user to $Session
392
// The Session has no user or $login is given (and differs from current user), allow alternate authentication through Plugin:
393
if( ( $event_return = $Plugins->trigger_event_first_true( 'AlternateAuthentication' ) )
394
&& $Session->has_User() # the plugin should have attached the user to $Session
550
$Debuglog->add( 'User has been authenticated through plugin #'.$event_return['plugin_ID'].' (AlternateAuthentication)', 'login' );
551
397
$current_User = & $UserCache->get_by_ID( $Session->user_ID );
553
399
elseif( $login_required )
554
{ // User was not logged in at all, but login is required
555
$Debuglog->add( 'NOT logged in... (did not try)', 'login' );
401
// User was not logged in at all, but login is required
557
402
$Messages->add( T_('You must log in!'), 'login_error' );
562
407
// Check if the user needs to be validated, but is not yet:
563
// TODO: dh> this block prevents registration, if you are logged in already, but not validated!
564
// (e.g. when registered as "foo", you cannot register as "bar" until you logout (but there's no link in sight)
565
// or validate the "foo" account)
566
if( ! empty($current_User)
408
// @todo (0000) dh> this block prevents registration, if you are logged in already, but not
409
// validated! (e.g. when registered as "foo", you cannot register as "bar" until you logout
410
// (but there's no link in sight) or validate the "foo" account)
411
if( ! empty( $current_User )
567
412
&& ! $current_User->validated
568
&& $Settings->get('newusers_mustvalidate') // same check as in login.php
413
&& $Settings->get( 'newusers_mustvalidate' ) // same check as in login.php
569
414
&& param('action', 'string', '') != 'logout' ) // fp> TODO: non validated users should be automatically logged out
571
416
if( $action != 'req_validatemail' && $action != 'validatemail' )
572
{ // we're not in that action already:
418
// we're not in that action already:
573
419
$action = 'req_validatemail'; // for login.php
574
420
$Messages->add( T_('You must validate your email address before you can log in.'), 'login_error' );
578
{ // Trigger plugin event that allows the plugins to re-act on the login event:
579
if( empty($current_User) )
425
// Trigger plugin event that allows the plugins to re-act on the login event:
426
if( empty( $current_User ) )
581
428
$Plugins->trigger_event( 'AfterLoginAnonymousUser', array() );
604
453
// If there are "login_error" messages, they trigger the login form at the end of this file.
606
/* Login procedure }}} */
610
* User locale selection:
612
if( is_logged_in() && $current_User->get('locale') != $current_locale
613
&& !$locale_from_get )
614
{ // change locale to users preference
616
* User locale selection:
617
* TODO: this should get done before instantiating $current_User, because we already use T_() there...
619
locale_activate( $current_User->get('locale') );
620
if( $current_locale == $current_User->get('locale') )
455
// Login procedure ...
457
// User locale selection
458
if( is_logged_in() && $current_User->get( 'locale' ) != $current_locale && ! $locale_from_get )
460
// User locale selection: change locale to users preference
461
// @todo (0000) this should get done before instantiating $current_User, because we already use T_() there...
462
locale_activate( $current_User->get( 'locale' ) );
463
if( $current_locale == $current_User->get( 'locale' ) )
622
465
$default_locale = $current_locale;
623
$Debuglog->add( 'default_locale from user profile: '.$default_locale, 'locale' );
627
$Debuglog->add( 'locale from user profile could not be activated: '.$current_User->get('locale'), 'locale' );
632
// Init charset handling:
633
// TODO: dh> anything translated before this call might have encoding issues (e.g. the login form!)
469
// Init charset handling
470
// @todo (0000) dh> anything translated before this call might have encoding issues (e.g. the login form!)
634
471
init_charsets( $current_charset );
637
473
// Display login errors (and form). This uses $io_charset, so it's at the end.
639
475
if( $Messages->count( 'login_error' ) )