aboutsummaryrefslogtreecommitdiffstats
path: root/phpBB/includes/ucp
diff options
context:
space:
mode:
authorMeik Sievertsen <acydburn@phpbb.com>2009-10-04 18:14:59 +0000
committerMeik Sievertsen <acydburn@phpbb.com>2009-10-04 18:14:59 +0000
commit2e17e448deed073f8614bb555a8ef20c57291c2a (patch)
tree533007e53d3584d0887b0f639d0e673b1e15ea7a /phpBB/includes/ucp
parentbf8ac19eaa8d74f9dfd6d597190f5664e7339382 (diff)
downloadforums-2e17e448deed073f8614bb555a8ef20c57291c2a.tar
forums-2e17e448deed073f8614bb555a8ef20c57291c2a.tar.gz
forums-2e17e448deed073f8614bb555a8ef20c57291c2a.tar.bz2
forums-2e17e448deed073f8614bb555a8ef20c57291c2a.tar.xz
forums-2e17e448deed073f8614bb555a8ef20c57291c2a.zip
Copy 3.0.x branch to trunk
git-svn-id: file:///svn/phpbb/trunk@10211 89ea8834-ac86-4346-8a33-228a782c2dd0
Diffstat (limited to 'phpBB/includes/ucp')
-rw-r--r--phpBB/includes/ucp/info/ucp_attachments.php37
-rw-r--r--phpBB/includes/ucp/info/ucp_groups.php38
-rw-r--r--phpBB/includes/ucp/info/ucp_main.php40
-rw-r--r--phpBB/includes/ucp/info/ucp_pm.php40
-rw-r--r--phpBB/includes/ucp/info/ucp_prefs.php39
-rw-r--r--phpBB/includes/ucp/info/ucp_profile.php40
-rw-r--r--phpBB/includes/ucp/info/ucp_zebra.php38
-rw-r--r--phpBB/includes/ucp/ucp_activate.php143
-rw-r--r--phpBB/includes/ucp/ucp_attachments.php201
-rw-r--r--phpBB/includes/ucp/ucp_confirm.php50
-rw-r--r--phpBB/includes/ucp/ucp_groups.php1117
-rw-r--r--phpBB/includes/ucp/ucp_main.php835
-rw-r--r--phpBB/includes/ucp/ucp_pm.php416
-rw-r--r--phpBB/includes/ucp/ucp_pm_compose.php1290
-rw-r--r--phpBB/includes/ucp/ucp_pm_options.php836
-rw-r--r--phpBB/includes/ucp/ucp_pm_viewfolder.php509
-rw-r--r--phpBB/includes/ucp/ucp_pm_viewmessage.php320
-rw-r--r--phpBB/includes/ucp/ucp_prefs.php357
-rw-r--r--phpBB/includes/ucp/ucp_profile.php649
-rw-r--r--phpBB/includes/ucp/ucp_register.php503
-rw-r--r--phpBB/includes/ucp/ucp_remind.php125
-rw-r--r--phpBB/includes/ucp/ucp_resend.php170
-rw-r--r--phpBB/includes/ucp/ucp_zebra.php257
23 files changed, 8050 insertions, 0 deletions
diff --git a/phpBB/includes/ucp/info/ucp_attachments.php b/phpBB/includes/ucp/info/ucp_attachments.php
new file mode 100644
index 0000000000..84edce446c
--- /dev/null
+++ b/phpBB/includes/ucp/info/ucp_attachments.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @package module_install
+*/
+class ucp_attachments_info
+{
+ function module()
+ {
+ return array(
+ 'filename' => 'ucp_attachments',
+ 'title' => 'UCP_ATTACHMENTS',
+ 'version' => '1.0.0',
+ 'modes' => array(
+ 'attachments' => array('title' => 'UCP_MAIN_ATTACHMENTS', 'auth' => 'acl_u_attach', 'cat' => array('UCP_MAIN')),
+ ),
+ );
+ }
+
+ function install()
+ {
+ }
+
+ function uninstall()
+ {
+ }
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/info/ucp_groups.php b/phpBB/includes/ucp/info/ucp_groups.php
new file mode 100644
index 0000000000..2002123c50
--- /dev/null
+++ b/phpBB/includes/ucp/info/ucp_groups.php
@@ -0,0 +1,38 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @package module_install
+*/
+class ucp_groups_info
+{
+ function module()
+ {
+ return array(
+ 'filename' => 'ucp_groups',
+ 'title' => 'UCP_USERGROUPS',
+ 'version' => '1.0.0',
+ 'modes' => array(
+ 'membership' => array('title' => 'UCP_USERGROUPS_MEMBER', 'auth' => '', 'cat' => array('UCP_USERGROUPS')),
+ 'manage' => array('title' => 'UCP_USERGROUPS_MANAGE', 'auth' => '', 'cat' => array('UCP_USERGROUPS')),
+ ),
+ );
+ }
+
+ function install()
+ {
+ }
+
+ function uninstall()
+ {
+ }
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/info/ucp_main.php b/phpBB/includes/ucp/info/ucp_main.php
new file mode 100644
index 0000000000..722b7865e6
--- /dev/null
+++ b/phpBB/includes/ucp/info/ucp_main.php
@@ -0,0 +1,40 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @package module_install
+*/
+class ucp_main_info
+{
+ function module()
+ {
+ return array(
+ 'filename' => 'ucp_main',
+ 'title' => 'UCP_MAIN',
+ 'version' => '1.0.0',
+ 'modes' => array(
+ 'front' => array('title' => 'UCP_MAIN_FRONT', 'auth' => '', 'cat' => array('UCP_MAIN')),
+ 'subscribed' => array('title' => 'UCP_MAIN_SUBSCRIBED', 'auth' => '', 'cat' => array('UCP_MAIN')),
+ 'bookmarks' => array('title' => 'UCP_MAIN_BOOKMARKS', 'auth' => 'cfg_allow_bookmarks', 'cat' => array('UCP_MAIN')),
+ 'drafts' => array('title' => 'UCP_MAIN_DRAFTS', 'auth' => '', 'cat' => array('UCP_MAIN')),
+ ),
+ );
+ }
+
+ function install()
+ {
+ }
+
+ function uninstall()
+ {
+ }
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/info/ucp_pm.php b/phpBB/includes/ucp/info/ucp_pm.php
new file mode 100644
index 0000000000..ade12005c0
--- /dev/null
+++ b/phpBB/includes/ucp/info/ucp_pm.php
@@ -0,0 +1,40 @@
+<?php
+/**
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @package module_install
+*/
+class ucp_pm_info
+{
+ function module()
+ {
+ return array(
+ 'filename' => 'ucp_pm',
+ 'title' => 'UCP_PM',
+ 'version' => '1.0.0',
+ 'modes' => array(
+ 'view' => array('title' => 'UCP_PM_VIEW', 'auth' => 'cfg_allow_privmsg', 'display' => false, 'cat' => array('UCP_PM')),
+ 'compose' => array('title' => 'UCP_PM_COMPOSE', 'auth' => 'cfg_allow_privmsg', 'cat' => array('UCP_PM')),
+ 'drafts' => array('title' => 'UCP_PM_DRAFTS', 'auth' => 'cfg_allow_privmsg', 'cat' => array('UCP_PM')),
+ 'options' => array('title' => 'UCP_PM_OPTIONS', 'auth' => 'cfg_allow_privmsg', 'cat' => array('UCP_PM')),
+ 'popup' => array('title' => 'UCP_PM_POPUP_TITLE', 'auth' => 'cfg_allow_privmsg', 'display' => false, 'cat' => array('UCP_PM')),
+ ),
+ );
+ }
+
+ function install()
+ {
+ }
+
+ function uninstall()
+ {
+ }
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/info/ucp_prefs.php b/phpBB/includes/ucp/info/ucp_prefs.php
new file mode 100644
index 0000000000..58359e8a19
--- /dev/null
+++ b/phpBB/includes/ucp/info/ucp_prefs.php
@@ -0,0 +1,39 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @package module_install
+*/
+class ucp_prefs_info
+{
+ function module()
+ {
+ return array(
+ 'filename' => 'ucp_prefs',
+ 'title' => 'UCP_PREFS',
+ 'version' => '1.0.0',
+ 'modes' => array(
+ 'personal' => array('title' => 'UCP_PREFS_PERSONAL', 'auth' => '', 'cat' => array('UCP_PREFS')),
+ 'post' => array('title' => 'UCP_PREFS_POST', 'auth' => '', 'cat' => array('UCP_PREFS')),
+ 'view' => array('title' => 'UCP_PREFS_VIEW', 'auth' => '', 'cat' => array('UCP_PREFS')),
+ ),
+ );
+ }
+
+ function install()
+ {
+ }
+
+ function uninstall()
+ {
+ }
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/info/ucp_profile.php b/phpBB/includes/ucp/info/ucp_profile.php
new file mode 100644
index 0000000000..03a4c81f46
--- /dev/null
+++ b/phpBB/includes/ucp/info/ucp_profile.php
@@ -0,0 +1,40 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @package module_install
+*/
+class ucp_profile_info
+{
+ function module()
+ {
+ return array(
+ 'filename' => 'ucp_profile',
+ 'title' => 'UCP_PROFILE',
+ 'version' => '1.0.0',
+ 'modes' => array(
+ 'profile_info' => array('title' => 'UCP_PROFILE_PROFILE_INFO', 'auth' => '', 'cat' => array('UCP_PROFILE')),
+ 'signature' => array('title' => 'UCP_PROFILE_SIGNATURE', 'auth' => '', 'cat' => array('UCP_PROFILE')),
+ 'avatar' => array('title' => 'UCP_PROFILE_AVATAR', 'auth' => '', 'cat' => array('UCP_PROFILE')),
+ 'reg_details' => array('title' => 'UCP_PROFILE_REG_DETAILS', 'auth' => '', 'cat' => array('UCP_PROFILE')),
+ ),
+ );
+ }
+
+ function install()
+ {
+ }
+
+ function uninstall()
+ {
+ }
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/info/ucp_zebra.php b/phpBB/includes/ucp/info/ucp_zebra.php
new file mode 100644
index 0000000000..5fc1f8bee7
--- /dev/null
+++ b/phpBB/includes/ucp/info/ucp_zebra.php
@@ -0,0 +1,38 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @package module_install
+*/
+class ucp_zebra_info
+{
+ function module()
+ {
+ return array(
+ 'filename' => 'ucp_zebra',
+ 'title' => 'UCP_ZEBRA',
+ 'version' => '1.0.0',
+ 'modes' => array(
+ 'friends' => array('title' => 'UCP_ZEBRA_FRIENDS', 'auth' => '', 'cat' => array('UCP_ZEBRA')),
+ 'foes' => array('title' => 'UCP_ZEBRA_FOES', 'auth' => '', 'cat' => array('UCP_ZEBRA')),
+ ),
+ );
+ }
+
+ function install()
+ {
+ }
+
+ function uninstall()
+ {
+ }
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/ucp_activate.php b/phpBB/includes/ucp/ucp_activate.php
new file mode 100644
index 0000000000..8debaabf31
--- /dev/null
+++ b/phpBB/includes/ucp/ucp_activate.php
@@ -0,0 +1,143 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* ucp_activate
+* User activation
+* @package ucp
+*/
+class ucp_activate
+{
+ var $u_action;
+
+ function main($id, $mode)
+ {
+ global $config, $phpbb_root_path, $phpEx;
+ global $db, $user, $auth, $template;
+
+ $user_id = request_var('u', 0);
+ $key = request_var('k', '');
+
+ $sql = 'SELECT user_id, username, user_type, user_email, user_newpasswd, user_lang, user_notify_type, user_actkey, user_inactive_reason
+ FROM ' . USERS_TABLE . "
+ WHERE user_id = $user_id";
+ $result = $db->sql_query($sql);
+ $user_row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if (!$user_row)
+ {
+ trigger_error('NO_USER');
+ }
+
+ if ($user_row['user_type'] <> USER_INACTIVE && !$user_row['user_newpasswd'])
+ {
+ meta_refresh(3, append_sid("{$phpbb_root_path}index.$phpEx"));
+ trigger_error('ALREADY_ACTIVATED');
+ }
+
+ if (($user_row['user_inactive_reason'] == INACTIVE_MANUAL) || $user_row['user_actkey'] != $key)
+ {
+ trigger_error('WRONG_ACTIVATION');
+ }
+
+ // Do not allow activating by non administrators when admin activation is on
+ // Only activation type the user should be able to do is INACTIVE_REMIND
+ // or activate a new password which is not an activation state :@
+ if (!$user_row['user_newpasswd'] && $user_row['user_inactive_reason'] != INACTIVE_REMIND && $config['require_activation'] == USER_ACTIVATION_ADMIN && !$auth->acl_get('a_user'))
+ {
+ if (!$user->data['is_registered'])
+ {
+ login_box('', $user->lang['NO_AUTH_OPERATION']);
+ }
+ trigger_error('NO_AUTH_OPERATION');
+ }
+
+ $update_password = ($user_row['user_newpasswd']) ? true : false;
+
+ if ($update_password)
+ {
+ $sql_ary = array(
+ 'user_actkey' => '',
+ 'user_password' => $user_row['user_newpasswd'],
+ 'user_newpasswd' => '',
+ 'user_pass_convert' => 0,
+ 'user_login_attempts' => 0,
+ );
+
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
+ WHERE user_id = ' . $user_row['user_id'];
+ $db->sql_query($sql);
+
+ add_log('user', $user_row['user_id'], 'LOG_USER_NEW_PASSWORD', $user_row['username']);
+ }
+
+ if (!$update_password)
+ {
+ include_once($phpbb_root_path . 'includes/functions_user.' . $phpEx);
+
+ user_active_flip('activate', $user_row['user_id']);
+
+ $sql = 'UPDATE ' . USERS_TABLE . "
+ SET user_actkey = ''
+ WHERE user_id = {$user_row['user_id']}";
+ $db->sql_query($sql);
+ }
+
+ if ($config['require_activation'] == USER_ACTIVATION_ADMIN && !$update_password)
+ {
+ include_once($phpbb_root_path . 'includes/functions_messenger.' . $phpEx);
+
+ $messenger = new messenger(false);
+
+ $messenger->template('admin_welcome_activated', $user_row['user_lang']);
+
+ $messenger->to($user_row['user_email'], $user_row['username']);
+
+ $messenger->headers('X-AntiAbuse: Board servername - ' . $config['server_name']);
+ $messenger->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']);
+ $messenger->headers('X-AntiAbuse: Username - ' . $user->data['username']);
+ $messenger->headers('X-AntiAbuse: User IP - ' . $user->ip);
+
+ $messenger->assign_vars(array(
+ 'USERNAME' => htmlspecialchars_decode($user_row['username']))
+ );
+
+ $messenger->send($user_row['user_notify_type']);
+
+ $message = 'ACCOUNT_ACTIVE_ADMIN';
+ }
+ else
+ {
+ if (!$update_password)
+ {
+ $message = ($user_row['user_inactive_reason'] == INACTIVE_PROFILE) ? 'ACCOUNT_ACTIVE_PROFILE' : 'ACCOUNT_ACTIVE';
+ }
+ else
+ {
+ $message = 'PASSWORD_ACTIVATED';
+ }
+ }
+
+ meta_refresh(3, append_sid("{$phpbb_root_path}index.$phpEx"));
+ trigger_error($user->lang[$message]);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/ucp_attachments.php b/phpBB/includes/ucp/ucp_attachments.php
new file mode 100644
index 0000000000..b011b4f75d
--- /dev/null
+++ b/phpBB/includes/ucp/ucp_attachments.php
@@ -0,0 +1,201 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* ucp_attachments
+* User attachments
+* @package ucp
+*/
+class ucp_attachments
+{
+ var $u_action;
+
+ function main($id, $mode)
+ {
+ global $template, $user, $db, $config, $phpEx, $phpbb_root_path;
+
+ $start = request_var('start', 0);
+ $sort_key = request_var('sk', 'a');
+ $sort_dir = request_var('sd', 'a');
+
+ $delete = (isset($_POST['delete'])) ? true : false;
+ $confirm = (isset($_POST['confirm'])) ? true : false;
+ $delete_ids = array_keys(request_var('attachment', array(0)));
+
+ if ($delete && sizeof($delete_ids))
+ {
+ // Validate $delete_ids...
+ $sql = 'SELECT attach_id
+ FROM ' . ATTACHMENTS_TABLE . '
+ WHERE poster_id = ' . $user->data['user_id'] . '
+ AND is_orphan = 0
+ AND ' . $db->sql_in_set('attach_id', $delete_ids);
+ $result = $db->sql_query($sql);
+
+ $delete_ids = array();
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $delete_ids[] = $row['attach_id'];
+ }
+ $db->sql_freeresult($result);
+ }
+
+ if ($delete && sizeof($delete_ids))
+ {
+ $s_hidden_fields = array(
+ 'delete' => 1
+ );
+
+ foreach ($delete_ids as $attachment_id)
+ {
+ $s_hidden_fields['attachment'][$attachment_id] = 1;
+ }
+
+ if (confirm_box(true))
+ {
+ if (!function_exists('delete_attachments'))
+ {
+ include_once($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
+ }
+
+ delete_attachments('attach', $delete_ids);
+
+ meta_refresh(3, $this->u_action);
+ $message = ((sizeof($delete_ids) == 1) ? $user->lang['ATTACHMENT_DELETED'] : $user->lang['ATTACHMENTS_DELETED']) . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $this->u_action . '">', '</a>');
+ trigger_error($message);
+ }
+ else
+ {
+ confirm_box(false, (sizeof($delete_ids) == 1) ? 'DELETE_ATTACHMENT' : 'DELETE_ATTACHMENTS', build_hidden_fields($s_hidden_fields));
+ }
+ }
+
+ // Select box eventually
+ $sort_key_text = array('a' => $user->lang['SORT_FILENAME'], 'b' => $user->lang['SORT_COMMENT'], 'c' => $user->lang['SORT_EXTENSION'], 'd' => $user->lang['SORT_SIZE'], 'e' => $user->lang['SORT_DOWNLOADS'], 'f' => $user->lang['SORT_POST_TIME'], 'g' => $user->lang['SORT_TOPIC_TITLE']);
+ $sort_key_sql = array('a' => 'a.real_filename', 'b' => 'a.attach_comment', 'c' => 'a.extension', 'd' => 'a.filesize', 'e' => 'a.download_count', 'f' => 'a.filetime', 'g' => 't.topic_title');
+
+ $sort_dir_text = array('a' => $user->lang['ASCENDING'], 'd' => $user->lang['DESCENDING']);
+
+ $s_sort_key = '';
+ foreach ($sort_key_text as $key => $value)
+ {
+ $selected = ($sort_key == $key) ? ' selected="selected"' : '';
+ $s_sort_key .= '<option value="' . $key . '"' . $selected . '>' . $value . '</option>';
+ }
+
+ $s_sort_dir = '';
+ foreach ($sort_dir_text as $key => $value)
+ {
+ $selected = ($sort_dir == $key) ? ' selected="selected"' : '';
+ $s_sort_dir .= '<option value="' . $key . '"' . $selected . '>' . $value . '</option>';
+ }
+
+ if (!isset($sort_key_sql[$sort_key]))
+ {
+ $sort_key = 'a';
+ }
+
+ $order_by = $sort_key_sql[$sort_key] . ' ' . (($sort_dir == 'a') ? 'ASC' : 'DESC');
+
+ $sql = 'SELECT COUNT(attach_id) as num_attachments
+ FROM ' . ATTACHMENTS_TABLE . '
+ WHERE poster_id = ' . $user->data['user_id'] . '
+ AND is_orphan = 0';
+ $result = $db->sql_query($sql);
+ $num_attachments = $db->sql_fetchfield('num_attachments');
+ $db->sql_freeresult($result);
+
+ $sql = 'SELECT a.*, t.topic_title, p.message_subject as message_title
+ FROM ' . ATTACHMENTS_TABLE . ' a
+ LEFT JOIN ' . TOPICS_TABLE . ' t ON (a.topic_id = t.topic_id AND a.in_message = 0)
+ LEFT JOIN ' . PRIVMSGS_TABLE . ' p ON (a.post_msg_id = p.msg_id AND a.in_message = 1)
+ WHERE a.poster_id = ' . $user->data['user_id'] . "
+ AND a.is_orphan = 0
+ ORDER BY $order_by";
+ $result = $db->sql_query_limit($sql, $config['topics_per_page'], $start);
+
+ $row_count = 0;
+ if ($row = $db->sql_fetchrow($result))
+ {
+ $template->assign_var('S_ATTACHMENT_ROWS', true);
+
+ do
+ {
+ if ($row['in_message'])
+ {
+ $view_topic = append_sid("{$phpbb_root_path}ucp.$phpEx", "i=pm&amp;p={$row['post_msg_id']}");
+ }
+ else
+ {
+ $view_topic = append_sid("{$phpbb_root_path}viewtopic.$phpEx", "t={$row['topic_id']}&amp;p={$row['post_msg_id']}") . "#p{$row['post_msg_id']}";
+ }
+
+ $template->assign_block_vars('attachrow', array(
+ 'ROW_NUMBER' => $row_count + ($start + 1),
+ 'FILENAME' => $row['real_filename'],
+ 'COMMENT' => bbcode_nl2br($row['attach_comment']),
+ 'EXTENSION' => $row['extension'],
+ 'SIZE' => get_formatted_filesize($row['filesize']),
+ 'DOWNLOAD_COUNT' => $row['download_count'],
+ 'POST_TIME' => $user->format_date($row['filetime']),
+ 'TOPIC_TITLE' => ($row['in_message']) ? $row['message_title'] : $row['topic_title'],
+
+ 'ATTACH_ID' => $row['attach_id'],
+ 'POST_ID' => $row['post_msg_id'],
+ 'TOPIC_ID' => $row['topic_id'],
+
+ 'S_IN_MESSAGE' => $row['in_message'],
+
+ 'U_VIEW_ATTACHMENT' => append_sid("{$phpbb_root_path}download/file.$phpEx", 'id=' . $row['attach_id']),
+ 'U_VIEW_TOPIC' => $view_topic)
+ );
+
+ $row_count++;
+ }
+ while ($row = $db->sql_fetchrow($result));
+ }
+ $db->sql_freeresult($result);
+
+ $template->assign_vars(array(
+ 'PAGE_NUMBER' => on_page($num_attachments, $config['topics_per_page'], $start),
+ 'PAGINATION' => generate_pagination($this->u_action . "&amp;sk=$sort_key&amp;sd=$sort_dir", $num_attachments, $config['topics_per_page'], $start),
+ 'TOTAL_ATTACHMENTS' => $num_attachments,
+
+ 'L_TITLE' => $user->lang['UCP_ATTACHMENTS'],
+
+ 'U_SORT_FILENAME' => $this->u_action . "&amp;sk=a&amp;sd=" . (($sort_key == 'a' && $sort_dir == 'a') ? 'd' : 'a'),
+ 'U_SORT_FILE_COMMENT' => $this->u_action . "&amp;sk=b&amp;sd=" . (($sort_key == 'b' && $sort_dir == 'a') ? 'd' : 'a'),
+ 'U_SORT_EXTENSION' => $this->u_action . "&amp;sk=c&amp;sd=" . (($sort_key == 'c' && $sort_dir == 'a') ? 'd' : 'a'),
+ 'U_SORT_FILESIZE' => $this->u_action . "&amp;sk=d&amp;sd=" . (($sort_key == 'd' && $sort_dir == 'a') ? 'd' : 'a'),
+ 'U_SORT_DOWNLOADS' => $this->u_action . "&amp;sk=e&amp;sd=" . (($sort_key == 'e' && $sort_dir == 'a') ? 'd' : 'a'),
+ 'U_SORT_POST_TIME' => $this->u_action . "&amp;sk=f&amp;sd=" . (($sort_key == 'f' && $sort_dir == 'a') ? 'd' : 'a'),
+ 'U_SORT_TOPIC_TITLE' => $this->u_action . "&amp;sk=g&amp;sd=" . (($sort_key == 'g' && $sort_dir == 'a') ? 'd' : 'a'),
+
+ 'S_DISPLAY_MARK_ALL' => ($num_attachments) ? true : false,
+ 'S_DISPLAY_PAGINATION' => ($num_attachments) ? true : false,
+ 'S_UCP_ACTION' => $this->u_action,
+ 'S_SORT_OPTIONS' => $s_sort_key,
+ 'S_ORDER_SELECT' => $s_sort_dir)
+ );
+
+ $this->tpl_name = 'ucp_attachments';
+ $this->page_title = 'UCP_ATTACHMENTS';
+ }
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/ucp_confirm.php b/phpBB/includes/ucp/ucp_confirm.php
new file mode 100644
index 0000000000..445f7c7d2a
--- /dev/null
+++ b/phpBB/includes/ucp/ucp_confirm.php
@@ -0,0 +1,50 @@
+<?php
+/**
+*
+* @package VC
+* @version $Id$
+* @copyright (c) 2005 2008 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* ucp_confirm
+* Visual confirmation
+*
+* Note to potential users of this code ...
+*
+* Remember this is released under the _GPL_ and is subject
+* to that licence. Do not incorporate this within software
+* released or distributed in any way under a licence other
+* than the GPL. We will be watching ... ;)
+*
+* @package VC
+*/
+class ucp_confirm
+{
+ var $u_action;
+
+ function main($id, $mode)
+ {
+ global $db, $user, $phpbb_root_path, $config, $phpEx;
+
+ include($phpbb_root_path . 'includes/captcha/captcha_factory.' . $phpEx);
+ $captcha = phpbb_captcha_factory::get_instance($config['captcha_plugin']);
+ $captcha->init(request_var('type', 0));
+ $captcha->execute();
+
+ garbage_collection();
+ exit_handler();
+ }
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/ucp_groups.php b/phpBB/includes/ucp/ucp_groups.php
new file mode 100644
index 0000000000..423d9b718a
--- /dev/null
+++ b/phpBB/includes/ucp/ucp_groups.php
@@ -0,0 +1,1117 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* ucp_groups
+* @package ucp
+*/
+class ucp_groups
+{
+ var $u_action;
+
+ function main($id, $mode)
+ {
+ global $config, $phpbb_root_path, $phpEx;
+ global $db, $user, $auth, $cache, $template;
+
+ $user->add_lang('groups');
+
+ $return_page = '<br /><br />' . sprintf($user->lang['RETURN_PAGE'], '<a href="' . $this->u_action . '">', '</a>');
+
+ $mark_ary = request_var('mark', array(0));
+ $submit = (!empty($_POST['submit'])) ? true : false;
+ $delete = (!empty($_POST['delete'])) ? true : false;
+ $error = $data = array();
+
+ switch ($mode)
+ {
+ case 'membership':
+
+ $this->page_title = 'UCP_USERGROUPS_MEMBER';
+
+ if ($submit || isset($_POST['change_default']))
+ {
+ $action = (isset($_POST['change_default'])) ? 'change_default' : request_var('action', '');
+ $group_id = ($action == 'change_default') ? request_var('default', 0) : request_var('selected', 0);
+
+ if (!$group_id)
+ {
+ trigger_error('NO_GROUP_SELECTED');
+ }
+
+ $sql = 'SELECT group_id, group_name, group_type
+ FROM ' . GROUPS_TABLE . "
+ WHERE group_id IN ($group_id, {$user->data['group_id']})";
+ $result = $db->sql_query($sql);
+
+ $group_row = array();
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $row['group_name'] = ($row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $row['group_name']] : $row['group_name'];
+ $group_row[$row['group_id']] = $row;
+ }
+ $db->sql_freeresult($result);
+
+ if (!sizeof($group_row))
+ {
+ trigger_error('GROUP_NOT_EXIST');
+ }
+
+ switch ($action)
+ {
+ case 'change_default':
+ // User already having this group set as default?
+ if ($group_id == $user->data['group_id'])
+ {
+ trigger_error($user->lang['ALREADY_DEFAULT_GROUP'] . $return_page);
+ }
+
+ if (!$auth->acl_get('u_chggrp'))
+ {
+ trigger_error($user->lang['NOT_AUTHORISED'] . $return_page);
+ }
+
+ // User needs to be member of the group in order to make it default
+ if (!group_memberships($group_id, $user->data['user_id'], true))
+ {
+ trigger_error($user->lang['NOT_MEMBER_OF_GROUP'] . $return_page);
+ }
+
+ if (confirm_box(true))
+ {
+ group_user_attributes('default', $group_id, $user->data['user_id']);
+
+ add_log('user', $user->data['user_id'], 'LOG_USER_GROUP_CHANGE', sprintf($user->lang['USER_GROUP_CHANGE'], $group_row[$user->data['group_id']]['group_name'], $group_row[$group_id]['group_name']));
+
+ meta_refresh(3, $this->u_action);
+ trigger_error($user->lang['CHANGED_DEFAULT_GROUP'] . $return_page);
+ }
+ else
+ {
+ $s_hidden_fields = array(
+ 'default' => $group_id,
+ 'change_default'=> true
+ );
+
+ confirm_box(false, sprintf($user->lang['GROUP_CHANGE_DEFAULT'], $group_row[$group_id]['group_name']), build_hidden_fields($s_hidden_fields));
+ }
+
+ break;
+
+ case 'resign':
+
+ // User tries to resign from default group but is not allowed to change it?
+ if ($group_id == $user->data['group_id'] && !$auth->acl_get('u_chggrp'))
+ {
+ trigger_error($user->lang['NOT_RESIGN_FROM_DEFAULT_GROUP'] . $return_page);
+ }
+
+ if (!($row = group_memberships($group_id, $user->data['user_id'])))
+ {
+ trigger_error($user->lang['NOT_MEMBER_OF_GROUP'] . $return_page);
+ }
+ list(, $row) = each($row);
+
+ $sql = 'SELECT group_type
+ FROM ' . GROUPS_TABLE . '
+ WHERE group_id = ' . $group_id;
+ $result = $db->sql_query($sql);
+ $group_type = (int) $db->sql_fetchfield('group_type');
+ $db->sql_freeresult($result);
+
+ if ($group_type != GROUP_OPEN && $group_type != GROUP_FREE)
+ {
+ trigger_error($user->lang['CANNOT_RESIGN_GROUP'] . $return_page);
+ }
+
+ if (confirm_box(true))
+ {
+ group_user_del($group_id, $user->data['user_id']);
+
+ add_log('user', $user->data['user_id'], 'LOG_USER_GROUP_RESIGN', $group_row[$group_id]['group_name']);
+
+ meta_refresh(3, $this->u_action);
+ trigger_error($user->lang[($row['user_pending']) ? 'GROUP_RESIGNED_PENDING' : 'GROUP_RESIGNED_MEMBERSHIP'] . $return_page);
+ }
+ else
+ {
+ $s_hidden_fields = array(
+ 'selected' => $group_id,
+ 'action' => 'resign',
+ 'submit' => true
+ );
+
+ confirm_box(false, ($row['user_pending']) ? 'GROUP_RESIGN_PENDING' : 'GROUP_RESIGN_MEMBERSHIP', build_hidden_fields($s_hidden_fields));
+ }
+
+ break;
+
+ case 'join':
+
+ $sql = 'SELECT ug.*, u.username, u.username_clean, u.user_email
+ FROM ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . ' u
+ WHERE ug.user_id = u.user_id
+ AND ug.group_id = ' . $group_id . '
+ AND ug.user_id = ' . $user->data['user_id'];
+ $result = $db->sql_query($sql);
+ $row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if ($row)
+ {
+ if ($row['user_pending'])
+ {
+ trigger_error($user->lang['ALREADY_IN_GROUP_PENDING'] . $return_page);
+ }
+
+ trigger_error($user->lang['ALREADY_IN_GROUP'] . $return_page);
+ }
+
+ // Check permission to join (open group or request)
+ if ($group_row[$group_id]['group_type'] != GROUP_OPEN && $group_row[$group_id]['group_type'] != GROUP_FREE)
+ {
+ trigger_error($user->lang['CANNOT_JOIN_GROUP'] . $return_page);
+ }
+
+ if (confirm_box(true))
+ {
+ if ($group_row[$group_id]['group_type'] == GROUP_FREE)
+ {
+ group_user_add($group_id, $user->data['user_id']);
+
+ $email_template = 'group_added';
+ }
+ else
+ {
+ group_user_add($group_id, $user->data['user_id'], false, false, false, 0, 1);
+
+ $email_template = 'group_request';
+ }
+
+ include_once($phpbb_root_path . 'includes/functions_messenger.' . $phpEx);
+ $messenger = new messenger();
+
+ $sql = 'SELECT u.username, u.username_clean, u.user_email, u.user_notify_type, u.user_jabber, u.user_lang
+ FROM ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . ' u
+ WHERE ug.user_id = u.user_id
+ AND ' . (($group_row[$group_id]['group_type'] == GROUP_FREE) ? "ug.user_id = {$user->data['user_id']}" : 'ug.group_leader = 1') . "
+ AND ug.group_id = $group_id";
+ $result = $db->sql_query($sql);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $messenger->template($email_template, $row['user_lang']);
+
+ $messenger->to($row['user_email'], $row['username']);
+ $messenger->im($row['user_jabber'], $row['username']);
+
+ $messenger->assign_vars(array(
+ 'USERNAME' => htmlspecialchars_decode($row['username']),
+ 'GROUP_NAME' => htmlspecialchars_decode($group_row[$group_id]['group_name']),
+ 'REQUEST_USERNAME' => $user->data['username'],
+
+ 'U_PENDING' => generate_board_url() . "/ucp.$phpEx?i=groups&mode=manage&action=list&g=$group_id",
+ 'U_GROUP' => generate_board_url() . "/memberlist.$phpEx?mode=group&g=$group_id")
+ );
+
+ $messenger->send($row['user_notify_type']);
+ }
+ $db->sql_freeresult($result);
+
+ $messenger->save_queue();
+
+ add_log('user', $user->data['user_id'], 'LOG_USER_GROUP_JOIN' . (($group_row[$group_id]['group_type'] == GROUP_FREE) ? '' : '_PENDING'), $group_row[$group_id]['group_name']);
+
+ meta_refresh(3, $this->u_action);
+ trigger_error($user->lang[($group_row[$group_id]['group_type'] == GROUP_FREE) ? 'GROUP_JOINED' : 'GROUP_JOINED_PENDING'] . $return_page);
+ }
+ else
+ {
+ $s_hidden_fields = array(
+ 'selected' => $group_id,
+ 'action' => 'join',
+ 'submit' => true
+ );
+
+ confirm_box(false, ($group_row[$group_id]['group_type'] == GROUP_FREE) ? 'GROUP_JOIN' : 'GROUP_JOIN_PENDING', build_hidden_fields($s_hidden_fields));
+ }
+
+ break;
+
+ case 'demote':
+
+ if (!($row = group_memberships($group_id, $user->data['user_id'])))
+ {
+ trigger_error($user->lang['NOT_MEMBER_OF_GROUP'] . $return_page);
+ }
+ list(, $row) = each($row);
+
+ if (!$row['group_leader'])
+ {
+ trigger_error($user->lang['NOT_LEADER_OF_GROUP'] . $return_page);
+ }
+
+ if (confirm_box(true))
+ {
+ group_user_attributes('demote', $group_id, $user->data['user_id']);
+
+ add_log('user', $user->data['user_id'], 'LOG_USER_GROUP_DEMOTE', $group_row[$group_id]['group_name']);
+
+ meta_refresh(3, $this->u_action);
+ trigger_error($user->lang['USER_GROUP_DEMOTED'] . $return_page);
+ }
+ else
+ {
+ $s_hidden_fields = array(
+ 'selected' => $group_id,
+ 'action' => 'demote',
+ 'submit' => true
+ );
+
+ confirm_box(false, 'USER_GROUP_DEMOTE', build_hidden_fields($s_hidden_fields));
+ }
+
+ break;
+ }
+ }
+
+ $sql = 'SELECT g.*, ug.group_leader, ug.user_pending
+ FROM ' . GROUPS_TABLE . ' g, ' . USER_GROUP_TABLE . ' ug
+ WHERE ug.user_id = ' . $user->data['user_id'] . '
+ AND g.group_id = ug.group_id
+ ORDER BY g.group_type DESC, g.group_name';
+ $result = $db->sql_query($sql);
+
+ $group_id_ary = array();
+ $leader_count = $member_count = $pending_count = 0;
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $block = ($row['group_leader']) ? 'leader' : (($row['user_pending']) ? 'pending' : 'member');
+
+ switch ($row['group_type'])
+ {
+ case GROUP_OPEN:
+ $group_status = 'OPEN';
+ break;
+
+ case GROUP_CLOSED:
+ $group_status = 'CLOSED';
+ break;
+
+ case GROUP_HIDDEN:
+ $group_status = 'HIDDEN';
+ break;
+
+ case GROUP_SPECIAL:
+ $group_status = 'SPECIAL';
+ break;
+
+ case GROUP_FREE:
+ $group_status = 'FREE';
+ break;
+ }
+
+ $template->assign_block_vars($block, array(
+ 'GROUP_ID' => $row['group_id'],
+ 'GROUP_NAME' => ($row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $row['group_name']] : $row['group_name'],
+ 'GROUP_DESC' => ($row['group_type'] <> GROUP_SPECIAL) ? generate_text_for_display($row['group_desc'], $row['group_desc_uid'], $row['group_desc_bitfield'], $row['group_desc_options']) : $user->lang['GROUP_IS_SPECIAL'],
+ 'GROUP_SPECIAL' => ($row['group_type'] <> GROUP_SPECIAL) ? false : true,
+ 'GROUP_STATUS' => $user->lang['GROUP_IS_' . $group_status],
+ 'GROUP_COLOUR' => $row['group_colour'],
+
+ 'U_VIEW_GROUP' => append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=group&amp;g=' . $row['group_id']),
+
+ 'S_GROUP_DEFAULT' => ($row['group_id'] == $user->data['group_id']) ? true : false,
+ 'S_ROW_COUNT' => ${$block . '_count'}++)
+ );
+
+ $group_id_ary[] = (int) $row['group_id'];
+ }
+ $db->sql_freeresult($result);
+
+ // Hide hidden groups unless user is an admin with group privileges
+ $sql_and = ($auth->acl_gets('a_group', 'a_groupadd', 'a_groupdel')) ? '<> ' . GROUP_SPECIAL : 'NOT IN (' . GROUP_SPECIAL . ', ' . GROUP_HIDDEN . ')';
+
+ $sql = 'SELECT group_id, group_name, group_colour, group_desc, group_desc_uid, group_desc_bitfield, group_desc_options, group_type, group_founder_manage
+ FROM ' . GROUPS_TABLE . '
+ WHERE ' . ((sizeof($group_id_ary)) ? $db->sql_in_set('group_id', $group_id_ary, true) . ' AND ' : '') . "
+ group_type $sql_and
+ ORDER BY group_type DESC, group_name";
+ $result = $db->sql_query($sql);
+
+ $nonmember_count = 0;
+ while ($row = $db->sql_fetchrow($result))
+ {
+ switch ($row['group_type'])
+ {
+ case GROUP_OPEN:
+ $group_status = 'OPEN';
+ break;
+
+ case GROUP_CLOSED:
+ $group_status = 'CLOSED';
+ break;
+
+ case GROUP_HIDDEN:
+ $group_status = 'HIDDEN';
+ break;
+
+ case GROUP_SPECIAL:
+ $group_status = 'SPECIAL';
+ break;
+
+ case GROUP_FREE:
+ $group_status = 'FREE';
+ break;
+ }
+
+ $template->assign_block_vars('nonmember', array(
+ 'GROUP_ID' => $row['group_id'],
+ 'GROUP_NAME' => ($row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $row['group_name']] : $row['group_name'],
+ 'GROUP_DESC' => ($row['group_type'] <> GROUP_SPECIAL) ? generate_text_for_display($row['group_desc'], $row['group_desc_uid'], $row['group_desc_bitfield'], $row['group_desc_options']) : $user->lang['GROUP_IS_SPECIAL'],
+ 'GROUP_SPECIAL' => ($row['group_type'] <> GROUP_SPECIAL) ? false : true,
+ 'GROUP_CLOSED' => ($row['group_type'] <> GROUP_CLOSED || $auth->acl_gets('a_group', 'a_groupadd', 'a_groupdel')) ? false : true,
+ 'GROUP_STATUS' => $user->lang['GROUP_IS_' . $group_status],
+ 'S_CAN_JOIN' => ($row['group_type'] == GROUP_OPEN || $row['group_type'] == GROUP_FREE) ? true : false,
+ 'GROUP_COLOUR' => $row['group_colour'],
+
+ 'U_VIEW_GROUP' => append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=group&amp;g=' . $row['group_id']),
+
+ 'S_ROW_COUNT' => $nonmember_count++)
+ );
+ }
+ $db->sql_freeresult($result);
+
+ $template->assign_vars(array(
+ 'S_CHANGE_DEFAULT' => ($auth->acl_get('u_chggrp')) ? true : false,
+ 'S_LEADER_COUNT' => $leader_count,
+ 'S_MEMBER_COUNT' => $member_count,
+ 'S_PENDING_COUNT' => $pending_count,
+ 'S_NONMEMBER_COUNT' => $nonmember_count,
+
+ 'S_UCP_ACTION' => $this->u_action)
+ );
+
+ break;
+
+ case 'manage':
+
+ $this->page_title = 'UCP_USERGROUPS_MANAGE';
+ $action = (isset($_POST['addusers'])) ? 'addusers' : request_var('action', '');
+ $group_id = request_var('g', 0);
+
+ include($phpbb_root_path . 'includes/functions_display.' . $phpEx);
+
+ add_form_key('ucp_groups');
+
+ if ($group_id)
+ {
+ $sql = 'SELECT *
+ FROM ' . GROUPS_TABLE . "
+ WHERE group_id = $group_id";
+ $result = $db->sql_query($sql);
+ $group_row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if (!$group_row)
+ {
+ trigger_error($user->lang['NO_GROUP'] . $return_page);
+ }
+
+ // Check if the user is allowed to manage this group if set to founder only.
+ if ($user->data['user_type'] != USER_FOUNDER && $group_row['group_founder_manage'])
+ {
+ trigger_error($user->lang['NOT_ALLOWED_MANAGE_GROUP'] . $return_page, E_USER_WARNING);
+ }
+
+ $group_name = $group_row['group_name'];
+ $group_type = $group_row['group_type'];
+
+ $avatar_img = (!empty($group_row['group_avatar'])) ? get_user_avatar($group_row['group_avatar'], $group_row['group_avatar_type'], $group_row['group_avatar_width'], $group_row['group_avatar_height'], 'GROUP_AVATAR') : '<img src="' . $phpbb_root_path . 'adm/images/no_avatar.gif" alt="" />';
+
+ $template->assign_vars(array(
+ 'GROUP_NAME' => ($group_type == GROUP_SPECIAL) ? $user->lang['G_' . $group_name] : $group_name,
+ 'GROUP_INTERNAL_NAME' => $group_name,
+ 'GROUP_COLOUR' => (isset($group_row['group_colour'])) ? $group_row['group_colour'] : '',
+ 'GROUP_DESC_DISP' => generate_text_for_display($group_row['group_desc'], $group_row['group_desc_uid'], $group_row['group_desc_bitfield'], $group_row['group_desc_options']),
+ 'GROUP_TYPE' => $group_row['group_type'],
+
+ 'AVATAR' => $avatar_img,
+ 'AVATAR_IMAGE' => $avatar_img,
+ 'AVATAR_WIDTH' => (isset($group_row['group_avatar_width'])) ? $group_row['group_avatar_width'] : '',
+ 'AVATAR_HEIGHT' => (isset($group_row['group_avatar_height'])) ? $group_row['group_avatar_height'] : '',
+ ));
+ }
+
+ switch ($action)
+ {
+ case 'edit':
+
+ if (!$group_id)
+ {
+ trigger_error($user->lang['NO_GROUP'] . $return_page);
+ }
+
+ if (!($row = group_memberships($group_id, $user->data['user_id'])))
+ {
+ trigger_error($user->lang['NOT_MEMBER_OF_GROUP'] . $return_page);
+ }
+ list(, $row) = each($row);
+
+ if (!$row['group_leader'])
+ {
+ trigger_error($user->lang['NOT_LEADER_OF_GROUP'] . $return_page);
+ }
+
+ $file_uploads = (@ini_get('file_uploads') || strtolower(@ini_get('file_uploads')) == 'on') ? true : false;
+ $user->add_lang(array('acp/groups', 'acp/common'));
+
+ $data = $submit_ary = array();
+
+ $update = (isset($_POST['update'])) ? true : false;
+
+ $error = array();
+
+ $avatar_select = basename(request_var('avatar_select', ''));
+ $category = basename(request_var('category', ''));
+
+ $can_upload = (file_exists($phpbb_root_path . $config['avatar_path']) && @is_writable($phpbb_root_path . $config['avatar_path']) && $file_uploads) ? true : false;
+
+ // Did we submit?
+ if ($update)
+ {
+ $group_name = utf8_normalize_nfc(request_var('group_name', '', true));
+ $group_desc = utf8_normalize_nfc(request_var('group_desc', '', true));
+ $group_type = request_var('group_type', GROUP_FREE);
+
+ $allow_desc_bbcode = request_var('desc_parse_bbcode', false);
+ $allow_desc_urls = request_var('desc_parse_urls', false);
+ $allow_desc_smilies = request_var('desc_parse_smilies', false);
+
+ $submit_ary = array(
+ 'colour' => request_var('group_colour', ''),
+ 'rank' => request_var('group_rank', 0),
+ 'receive_pm' => isset($_REQUEST['group_receive_pm']) ? 1 : 0,
+ 'message_limit' => request_var('group_message_limit', 0),
+ 'max_recipients'=> request_var('group_max_recipients', 0),
+ );
+
+ $data['uploadurl'] = request_var('uploadurl', '');
+ $data['remotelink'] = request_var('remotelink', '');
+ $data['width'] = request_var('width', '');
+ $data['height'] = request_var('height', '');
+ $delete = request_var('delete', '');
+
+ if (!empty($_FILES['uploadfile']['tmp_name']) || $data['uploadurl'] || $data['remotelink'])
+ {
+ // Avatar stuff
+ $var_ary = array(
+ 'uploadurl' => array('string', true, 5, 255),
+ 'remotelink' => array('string', true, 5, 255),
+ 'width' => array('string', true, 1, 3),
+ 'height' => array('string', true, 1, 3),
+ );
+
+ if (!($error = validate_data($data, $var_ary)))
+ {
+ $data['user_id'] = "g$group_id";
+
+ if ((!empty($_FILES['uploadfile']['tmp_name']) || $data['uploadurl']) && $can_upload)
+ {
+ list($submit_ary['avatar_type'], $submit_ary['avatar'], $submit_ary['avatar_width'], $submit_ary['avatar_height']) = avatar_upload($data, $error);
+ }
+ else if ($data['remotelink'])
+ {
+ list($submit_ary['avatar_type'], $submit_ary['avatar'], $submit_ary['avatar_width'], $submit_ary['avatar_height']) = avatar_remote($data, $error);
+ }
+ }
+ }
+ else if ($avatar_select && $config['allow_avatar_local'])
+ {
+ // check avatar gallery
+ if (is_dir($phpbb_root_path . $config['avatar_gallery_path'] . '/' . $category))
+ {
+ $submit_ary['avatar_type'] = AVATAR_GALLERY;
+
+ list($submit_ary['avatar_width'], $submit_ary['avatar_height']) = getimagesize($phpbb_root_path . $config['avatar_gallery_path'] . '/' . $category . '/' . $avatar_select);
+ $submit_ary['avatar'] = $category . '/' . $avatar_select;
+ }
+ }
+ else if ($delete)
+ {
+ $submit_ary['avatar'] = '';
+ $submit_ary['avatar_type'] = $submit_ary['avatar_width'] = $submit_ary['avatar_height'] = 0;
+ }
+ else if ($data['width'] && $data['height'])
+ {
+ // Only update the dimensions?
+ if ($config['avatar_max_width'] || $config['avatar_max_height'])
+ {
+ if ($data['width'] > $config['avatar_max_width'] || $data['height'] > $config['avatar_max_height'])
+ {
+ $error[] = sprintf($user->lang['AVATAR_WRONG_SIZE'], $config['avatar_min_width'], $config['avatar_min_height'], $config['avatar_max_width'], $config['avatar_max_height'], $data['width'], $data['height']);
+ }
+ }
+
+ if (!sizeof($error))
+ {
+ if ($config['avatar_min_width'] || $config['avatar_min_height'])
+ {
+ if ($data['width'] < $config['avatar_min_width'] || $data['height'] < $config['avatar_min_height'])
+ {
+ $error[] = sprintf($user->lang['AVATAR_WRONG_SIZE'], $config['avatar_min_width'], $config['avatar_min_height'], $config['avatar_max_width'], $config['avatar_max_height'], $data['width'], $data['height']);
+ }
+ }
+ }
+
+ if (!sizeof($error))
+ {
+ $submit_ary['avatar_width'] = $data['width'];
+ $submit_ary['avatar_height'] = $data['height'];
+ }
+ }
+
+ if ((isset($submit_ary['avatar']) && $submit_ary['avatar'] && (!isset($group_row['group_avatar']))) || $delete)
+ {
+ if (isset($group_row['group_avatar']) && $group_row['group_avatar'])
+ {
+ avatar_delete('group', $group_row, true);
+ }
+ }
+
+ if (!check_form_key('ucp_groups'))
+ {
+ $error[] = $user->lang['FORM_INVALID'];
+ }
+
+ if (!sizeof($error))
+ {
+ // Only set the rank, colour, etc. if it's changed or if we're adding a new
+ // group. This prevents existing group members being updated if no changes
+ // were made.
+
+ $group_attributes = array();
+ $test_variables = array(
+ 'rank' => 'int',
+ 'colour' => 'string',
+ 'avatar' => 'string',
+ 'avatar_type' => 'int',
+ 'avatar_width' => 'int',
+ 'avatar_height' => 'int',
+ 'receive_pm' => 'int',
+ 'legend' => 'int',
+ 'message_limit' => 'int',
+ 'max_recipients'=> 'int',
+ );
+
+ foreach ($test_variables as $test => $type)
+ {
+ if (isset($submit_ary[$test]) && ($action == 'add' || $group_row['group_' . $test] != $submit_ary[$test]))
+ {
+ settype($submit_ary[$test], $type);
+ $group_attributes['group_' . $test] = $group_row['group_' . $test] = $submit_ary[$test];
+ }
+ }
+
+ if (!($error = group_create($group_id, $group_type, $group_name, $group_desc, $group_attributes, $allow_desc_bbcode, $allow_desc_urls, $allow_desc_smilies)))
+ {
+ $cache->destroy('sql', GROUPS_TABLE);
+
+ $message = ($action == 'edit') ? 'GROUP_UPDATED' : 'GROUP_CREATED';
+ trigger_error($user->lang[$message] . $return_page);
+ }
+ }
+
+ if (sizeof($error))
+ {
+ $group_rank = $submit_ary['rank'];
+
+ $group_desc_data = array(
+ 'text' => $group_desc,
+ 'allow_bbcode' => $allow_desc_bbcode,
+ 'allow_smilies' => $allow_desc_smilies,
+ 'allow_urls' => $allow_desc_urls
+ );
+ }
+ }
+ else if (!$group_id)
+ {
+ $group_name = utf8_normalize_nfc(request_var('group_name', '', true));
+ $group_desc_data = array(
+ 'text' => '',
+ 'allow_bbcode' => true,
+ 'allow_smilies' => true,
+ 'allow_urls' => true
+ );
+ $group_rank = 0;
+ $group_type = GROUP_OPEN;
+ }
+ else
+ {
+ $group_desc_data = generate_text_for_edit($group_row['group_desc'], $group_row['group_desc_uid'], $group_row['group_desc_options']);
+ $group_rank = $group_row['group_rank'];
+ }
+
+ $sql = 'SELECT *
+ FROM ' . RANKS_TABLE . '
+ WHERE rank_special = 1
+ ORDER BY rank_title';
+ $result = $db->sql_query($sql);
+
+ $rank_options = '<option value="0"' . ((!$group_rank) ? ' selected="selected"' : '') . '>' . $user->lang['USER_DEFAULT'] . '</option>';
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $selected = ($group_rank && $row['rank_id'] == $group_rank) ? ' selected="selected"' : '';
+ $rank_options .= '<option value="' . $row['rank_id'] . '"' . $selected . '>' . $row['rank_title'] . '</option>';
+ }
+ $db->sql_freeresult($result);
+
+ $type_free = ($group_type == GROUP_FREE) ? ' checked="checked"' : '';
+ $type_open = ($group_type == GROUP_OPEN) ? ' checked="checked"' : '';
+ $type_closed = ($group_type == GROUP_CLOSED) ? ' checked="checked"' : '';
+ $type_hidden = ($group_type == GROUP_HIDDEN) ? ' checked="checked"' : '';
+
+ $display_gallery = (isset($_POST['display_gallery'])) ? true : false;
+
+ if ($config['allow_avatar'] && $config['allow_avatar_local'] && $display_gallery)
+ {
+ avatar_gallery($category, $avatar_select, 4);
+ }
+
+ $avatars_enabled = ($config['allow_avatar'] && (($can_upload && ($config['allow_avatar_upload'] || $config['allow_avatar_remote_upload'])) || ($config['allow_avatar_local'] || $config['allow_avatar_remote']))) ? true : false;
+
+ $template->assign_vars(array(
+ 'S_EDIT' => true,
+ 'S_INCLUDE_SWATCH' => true,
+ 'S_FORM_ENCTYPE' => ($config['allow_avatar'] && $can_upload && ($config['allow_avatar_upload'] || $config['allow_avatar_remote_upload'])) ? ' enctype="multipart/form-data"' : '',
+ 'S_ERROR' => (sizeof($error)) ? true : false,
+ 'S_SPECIAL_GROUP' => ($group_type == GROUP_SPECIAL) ? true : false,
+ 'S_AVATARS_ENABLED' => $avatars_enabled,
+ 'S_DISPLAY_GALLERY' => ($config['allow_avatar'] && $config['allow_avatar_local'] && !$display_gallery) ? true : false,
+ 'S_IN_GALLERY' => ($config['allow_avatar_local'] && $display_gallery) ? true : false,
+
+ 'S_UPLOAD_AVATAR_FILE' => ($config['allow_avatar'] && $config['allow_avatar_upload'] && $can_upload) ? true : false,
+ 'S_UPLOAD_AVATAR_URL' => ($config['allow_avatar'] && $config['allow_avatar_remote_upload'] && $can_upload) ? true : false,
+ 'S_LINK_AVATAR' => ($config['allow_avatar'] && $config['allow_avatar_remote']) ? true : false,
+
+ 'ERROR_MSG' => (sizeof($error)) ? implode('<br />', $error) : '',
+ 'GROUP_RECEIVE_PM' => (isset($group_row['group_receive_pm']) && $group_row['group_receive_pm']) ? ' checked="checked"' : '',
+ 'GROUP_MESSAGE_LIMIT' => (isset($group_row['group_message_limit'])) ? $group_row['group_message_limit'] : 0,
+ 'GROUP_MAX_RECIPIENTS' => (isset($group_row['group_max_recipients'])) ? $group_row['group_max_recipients'] : 0,
+
+ 'GROUP_DESC' => $group_desc_data['text'],
+ 'S_DESC_BBCODE_CHECKED' => $group_desc_data['allow_bbcode'],
+ 'S_DESC_URLS_CHECKED' => $group_desc_data['allow_urls'],
+ 'S_DESC_SMILIES_CHECKED'=> $group_desc_data['allow_smilies'],
+
+ 'S_RANK_OPTIONS' => $rank_options,
+ 'AVATAR_MAX_FILESIZE' => $config['avatar_filesize'],
+
+ 'GROUP_TYPE_FREE' => GROUP_FREE,
+ 'GROUP_TYPE_OPEN' => GROUP_OPEN,
+ 'GROUP_TYPE_CLOSED' => GROUP_CLOSED,
+ 'GROUP_TYPE_HIDDEN' => GROUP_HIDDEN,
+ 'GROUP_TYPE_SPECIAL' => GROUP_SPECIAL,
+
+ 'GROUP_FREE' => $type_free,
+ 'GROUP_OPEN' => $type_open,
+ 'GROUP_CLOSED' => $type_closed,
+ 'GROUP_HIDDEN' => $type_hidden,
+
+ 'U_SWATCH' => append_sid("{$phpbb_root_path}adm/swatch.$phpEx", 'form=ucp&amp;name=group_colour'),
+ 'S_UCP_ACTION' => $this->u_action . "&amp;action=$action&amp;g=$group_id",
+ 'L_AVATAR_EXPLAIN' => sprintf($user->lang['AVATAR_EXPLAIN'], $config['avatar_max_width'], $config['avatar_max_height'], $config['avatar_filesize'] / 1024),
+ ));
+
+ break;
+
+ case 'list':
+
+ if (!$group_id)
+ {
+ trigger_error($user->lang['NO_GROUP'] . $return_page);
+ }
+
+ if (!($row = group_memberships($group_id, $user->data['user_id'])))
+ {
+ trigger_error($user->lang['NOT_MEMBER_OF_GROUP'] . $return_page);
+ }
+ list(, $row) = each($row);
+
+ if (!$row['group_leader'])
+ {
+ trigger_error($user->lang['NOT_LEADER_OF_GROUP'] . $return_page);
+ }
+
+ $user->add_lang(array('acp/groups', 'acp/common'));
+ $start = request_var('start', 0);
+
+ // Grab the leaders - always, on every page...
+ $sql = 'SELECT u.user_id, u.username, u.username_clean, u.user_colour, u.user_regdate, u.user_posts, u.group_id, ug.group_leader, ug.user_pending
+ FROM ' . USERS_TABLE . ' u, ' . USER_GROUP_TABLE . " ug
+ WHERE ug.group_id = $group_id
+ AND u.user_id = ug.user_id
+ AND ug.group_leader = 1
+ ORDER BY ug.user_pending DESC, u.username_clean";
+ $result = $db->sql_query($sql);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $template->assign_block_vars('leader', array(
+ 'USERNAME' => $row['username'],
+ 'USERNAME_COLOUR' => $row['user_colour'],
+ 'USERNAME_FULL' => get_username_string('full', $row['user_id'], $row['username'], $row['user_colour']),
+ 'U_USER_VIEW' => get_username_string('profile', $row['user_id'], $row['username']),
+ 'S_GROUP_DEFAULT' => ($row['group_id'] == $group_id) ? true : false,
+ 'JOINED' => ($row['user_regdate']) ? $user->format_date($row['user_regdate']) : ' - ',
+ 'USER_POSTS' => $row['user_posts'],
+ 'USER_ID' => $row['user_id'])
+ );
+ }
+ $db->sql_freeresult($result);
+
+ // Total number of group members (non-leaders)
+ $sql = 'SELECT COUNT(user_id) AS total_members
+ FROM ' . USER_GROUP_TABLE . "
+ WHERE group_id = $group_id
+ AND group_leader = 0";
+ $result = $db->sql_query($sql);
+ $total_members = (int) $db->sql_fetchfield('total_members');
+ $db->sql_freeresult($result);
+
+ // Grab the members
+ $sql = 'SELECT u.user_id, u.username, u.username_clean, u.user_colour, u.user_regdate, u.user_posts, u.group_id, ug.group_leader, ug.user_pending
+ FROM ' . USERS_TABLE . ' u, ' . USER_GROUP_TABLE . " ug
+ WHERE ug.group_id = $group_id
+ AND u.user_id = ug.user_id
+ AND ug.group_leader = 0
+ ORDER BY ug.user_pending DESC, u.username_clean";
+ $result = $db->sql_query_limit($sql, $config['topics_per_page'], $start);
+
+ $pending = false;
+ $approved = false;
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ if ($row['user_pending'] && !$pending)
+ {
+ $template->assign_block_vars('member', array(
+ 'S_PENDING' => true)
+ );
+ $template->assign_var('S_PENDING_SET', true);
+
+ $pending = true;
+ }
+ else if (!$row['user_pending'] && !$approved)
+ {
+ $template->assign_block_vars('member', array(
+ 'S_APPROVED' => true)
+ );
+ $template->assign_var('S_APPROVED_SET', true);
+
+ $approved = true;
+ }
+
+ $template->assign_block_vars('member', array(
+ 'USERNAME' => $row['username'],
+ 'USERNAME_COLOUR' => $row['user_colour'],
+ 'USERNAME_FULL' => get_username_string('full', $row['user_id'], $row['username'], $row['user_colour']),
+ 'U_USER_VIEW' => get_username_string('profile', $row['user_id'], $row['username']),
+ 'S_GROUP_DEFAULT' => ($row['group_id'] == $group_id) ? true : false,
+ 'JOINED' => ($row['user_regdate']) ? $user->format_date($row['user_regdate']) : ' - ',
+ 'USER_POSTS' => $row['user_posts'],
+ 'USER_ID' => $row['user_id'])
+ );
+ }
+ $db->sql_freeresult($result);
+
+ $s_action_options = '';
+ $options = array('default' => 'DEFAULT', 'approve' => 'APPROVE', 'deleteusers' => 'DELETE');
+
+ foreach ($options as $option => $lang)
+ {
+ $s_action_options .= '<option value="' . $option . '">' . $user->lang['GROUP_' . $lang] . '</option>';
+ }
+
+ $template->assign_vars(array(
+ 'S_LIST' => true,
+ 'S_ACTION_OPTIONS' => $s_action_options,
+ 'S_ON_PAGE' => on_page($total_members, $config['topics_per_page'], $start),
+ 'PAGINATION' => generate_pagination($this->u_action . "&amp;action=$action&amp;g=$group_id", $total_members, $config['topics_per_page'], $start),
+
+ 'U_ACTION' => $this->u_action . "&amp;g=$group_id",
+ 'S_UCP_ACTION' => $this->u_action . "&amp;g=$group_id",
+ 'U_FIND_USERNAME' => append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=searchuser&amp;form=ucp&amp;field=usernames'),
+ ));
+
+ break;
+
+ case 'approve':
+
+ if (!$group_id)
+ {
+ trigger_error($user->lang['NO_GROUP'] . $return_page);
+ }
+
+ if (!($row = group_memberships($group_id, $user->data['user_id'])))
+ {
+ trigger_error($user->lang['NOT_MEMBER_OF_GROUP'] . $return_page);
+ }
+ list(, $row) = each($row);
+
+ if (!$row['group_leader'])
+ {
+ trigger_error($user->lang['NOT_LEADER_OF_GROUP'] . $return_page);
+ }
+
+ $user->add_lang('acp/groups');
+
+ // Approve, demote or promote
+ group_user_attributes('approve', $group_id, $mark_ary, false, false);
+
+ trigger_error($user->lang['USERS_APPROVED'] . '<br /><br />' . sprintf($user->lang['RETURN_PAGE'], '<a href="' . $this->u_action . '&amp;action=list&amp;g=' . $group_id . '">', '</a>'));
+
+ break;
+
+ case 'default':
+
+ if (!$group_id)
+ {
+ trigger_error($user->lang['NO_GROUP'] . $return_page);
+ }
+
+ if (!($row = group_memberships($group_id, $user->data['user_id'])))
+ {
+ trigger_error($user->lang['NOT_MEMBER_OF_GROUP'] . $return_page);
+ }
+ list(, $row) = each($row);
+
+ if (!$row['group_leader'])
+ {
+ trigger_error($user->lang['NOT_LEADER_OF_GROUP'] . $return_page);
+ }
+
+ $group_row['group_name'] = ($group_row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $group_row['group_name']] : $group_row['group_name'];
+
+ if (confirm_box(true))
+ {
+ if (!sizeof($mark_ary))
+ {
+ $start = 0;
+
+ do
+ {
+ $sql = 'SELECT user_id
+ FROM ' . USER_GROUP_TABLE . "
+ WHERE group_id = $group_id
+ ORDER BY user_id";
+ $result = $db->sql_query_limit($sql, 200, $start);
+
+ $mark_ary = array();
+ if ($row = $db->sql_fetchrow($result))
+ {
+ do
+ {
+ $mark_ary[] = $row['user_id'];
+ }
+ while ($row = $db->sql_fetchrow($result));
+
+ group_user_attributes('default', $group_id, $mark_ary, false, $group_row['group_name'], $group_row);
+
+ $start = (sizeof($mark_ary) < 200) ? 0 : $start + 200;
+ }
+ else
+ {
+ $start = 0;
+ }
+ $db->sql_freeresult($result);
+ }
+ while ($start);
+ }
+ else
+ {
+ group_user_attributes('default', $group_id, $mark_ary, false, $group_row['group_name'], $group_row);
+ }
+
+ $user->add_lang('acp/groups');
+
+ trigger_error($user->lang['GROUP_DEFS_UPDATED'] . '<br /><br />' . sprintf($user->lang['RETURN_PAGE'], '<a href="' . $this->u_action . '&amp;action=list&amp;g=' . $group_id . '">', '</a>'));
+ }
+ else
+ {
+ $user->add_lang('acp/common');
+
+ confirm_box(false, $user->lang['CONFIRM_OPERATION'], build_hidden_fields(array(
+ 'mark' => $mark_ary,
+ 'g' => $group_id,
+ 'i' => $id,
+ 'mode' => $mode,
+ 'action' => $action))
+ );
+ }
+
+ // redirect to last screen
+ redirect($this->u_action . '&amp;action=list&amp;g=' . $group_id);
+
+ break;
+
+ case 'deleteusers':
+
+ $user->add_lang(array('acp/groups', 'acp/common'));
+
+ if (!($row = group_memberships($group_id, $user->data['user_id'])))
+ {
+ trigger_error($user->lang['NOT_MEMBER_OF_GROUP'] . $return_page);
+ }
+ list(, $row) = each($row);
+
+ if (!$row['group_leader'])
+ {
+ trigger_error($user->lang['NOT_LEADER_OF_GROUP'] . $return_page);
+ }
+
+ $group_row['group_name'] = ($group_row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $group_row['group_name']] : $group_row['group_name'];
+
+ if (confirm_box(true))
+ {
+ if (!$group_id)
+ {
+ trigger_error($user->lang['NO_GROUP'] . $return_page);
+ }
+
+ $error = group_user_del($group_id, $mark_ary, false, $group_row['group_name']);
+
+ if ($error)
+ {
+ trigger_error($user->lang[$error] . '<br /><br />' . sprintf($user->lang['RETURN_PAGE'], '<a href="' . $this->u_action . '&amp;action=list&amp;g=' . $group_id . '">', '</a>'));
+ }
+
+ trigger_error($user->lang['GROUP_USERS_REMOVE'] . '<br /><br />' . sprintf($user->lang['RETURN_PAGE'], '<a href="' . $this->u_action . '&amp;action=list&amp;g=' . $group_id . '">', '</a>'));
+ }
+ else
+ {
+ confirm_box(false, $user->lang['CONFIRM_OPERATION'], build_hidden_fields(array(
+ 'mark' => $mark_ary,
+ 'g' => $group_id,
+ 'i' => $id,
+ 'mode' => $mode,
+ 'action' => $action))
+ );
+ }
+
+ // redirect to last screen
+ redirect($this->u_action . '&amp;action=list&amp;g=' . $group_id);
+
+ break;
+
+ case 'addusers':
+
+ $user->add_lang(array('acp/groups', 'acp/common'));
+
+ $names = utf8_normalize_nfc(request_var('usernames', '', true));
+
+ if (!$group_id)
+ {
+ trigger_error($user->lang['NO_GROUP'] . $return_page);
+ }
+
+ if (!$names)
+ {
+ trigger_error($user->lang['NO_USERS'] . $return_page);
+ }
+
+ if (!($row = group_memberships($group_id, $user->data['user_id'])))
+ {
+ trigger_error($user->lang['NOT_MEMBER_OF_GROUP'] . $return_page);
+ }
+ list(, $row) = each($row);
+
+ if (!$row['group_leader'])
+ {
+ trigger_error($user->lang['NOT_LEADER_OF_GROUP'] . $return_page);
+ }
+
+ $name_ary = array_unique(explode("\n", $names));
+ $group_name = ($group_row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $group_row['group_name']] : $group_row['group_name'];
+
+ $default = request_var('default', 0);
+
+ if (confirm_box(true))
+ {
+ // Add user/s to group
+ if ($error = group_user_add($group_id, false, $name_ary, $group_name, $default, 0, 0, $group_row))
+ {
+ trigger_error($user->lang[$error] . $return_page);
+ }
+
+ trigger_error($user->lang['GROUP_USERS_ADDED'] . '<br /><br />' . sprintf($user->lang['RETURN_PAGE'], '<a href="' . $this->u_action . '&amp;action=list&amp;g=' . $group_id . '">', '</a>'));
+ }
+ else
+ {
+ $s_hidden_fields = array(
+ 'default' => $default,
+ 'usernames' => $names,
+ 'g' => $group_id,
+ 'i' => $id,
+ 'mode' => $mode,
+ 'action' => $action
+ );
+ confirm_box(false, sprintf($user->lang['GROUP_CONFIRM_ADD_USER' . ((sizeof($name_ary) == 1) ? '' : 'S')], implode(', ', $name_ary)), build_hidden_fields($s_hidden_fields));
+ }
+
+ trigger_error($user->lang['NO_USERS_ADDED'] . '<br /><br />' . sprintf($user->lang['RETURN_PAGE'], '<a href="' . $this->u_action . '&amp;action=list&amp;g=' . $group_id . '">', '</a>'));
+
+ break;
+
+ default:
+ $user->add_lang('acp/common');
+
+ $sql = 'SELECT g.group_id, g.group_name, g.group_colour, g.group_desc, g.group_desc_uid, g.group_desc_bitfield, g.group_desc_options, g.group_type, ug.group_leader
+ FROM ' . GROUPS_TABLE . ' g, ' . USER_GROUP_TABLE . ' ug
+ WHERE ug.user_id = ' . $user->data['user_id'] . '
+ AND g.group_id = ug.group_id
+ AND ug.group_leader = 1
+ ORDER BY g.group_type DESC, g.group_name';
+ $result = $db->sql_query($sql);
+
+ while ($value = $db->sql_fetchrow($result))
+ {
+ $template->assign_block_vars('leader', array(
+ 'GROUP_NAME' => ($value['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $value['group_name']] : $value['group_name'],
+ 'GROUP_DESC' => generate_text_for_display($value['group_desc'], $value['group_desc_uid'], $value['group_desc_bitfield'], $value['group_desc_options']),
+ 'GROUP_TYPE' => $value['group_type'],
+ 'GROUP_ID' => $value['group_id'],
+ 'GROUP_COLOUR' => $value['group_colour'],
+
+ 'U_LIST' => $this->u_action . "&amp;action=list&amp;g={$value['group_id']}",
+ 'U_EDIT' => $this->u_action . "&amp;action=edit&amp;g={$value['group_id']}")
+ );
+ }
+ $db->sql_freeresult($result);
+
+ break;
+ }
+
+ break;
+ }
+
+ $this->tpl_name = 'ucp_groups_' . $mode;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/ucp_main.php b/phpBB/includes/ucp/ucp_main.php
new file mode 100644
index 0000000000..6ac2412ef0
--- /dev/null
+++ b/phpBB/includes/ucp/ucp_main.php
@@ -0,0 +1,835 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* ucp_main
+* UCP Front Panel
+* @package ucp
+*/
+class ucp_main
+{
+ var $p_master;
+ var $u_action;
+
+ function ucp_main(&$p_master)
+ {
+ $this->p_master = &$p_master;
+ }
+
+ function main($id, $mode)
+ {
+ global $config, $db, $user, $auth, $template, $phpbb_root_path, $phpEx;
+
+ switch ($mode)
+ {
+ case 'front':
+
+ $user->add_lang('memberlist');
+
+ $sql_from = TOPICS_TABLE . ' t ';
+ $sql_select = '';
+
+ if ($config['load_db_track'])
+ {
+ $sql_from .= ' LEFT JOIN ' . TOPICS_POSTED_TABLE . ' tp ON (tp.topic_id = t.topic_id
+ AND tp.user_id = ' . $user->data['user_id'] . ')';
+ $sql_select .= ', tp.topic_posted';
+ }
+
+ if ($config['load_db_lastread'])
+ {
+ $sql_from .= ' LEFT JOIN ' . TOPICS_TRACK_TABLE . ' tt ON (tt.topic_id = t.topic_id
+ AND tt.user_id = ' . $user->data['user_id'] . ')';
+ $sql_select .= ', tt.mark_time';
+ }
+
+ $topic_type = $user->lang['VIEW_TOPIC_GLOBAL'];
+ $folder = 'global_read';
+ $folder_new = 'global_unread';
+
+ // Get cleaned up list... return only those forums not having the f_read permission
+ $forum_ary = $auth->acl_getf('!f_read', true);
+ $forum_ary = array_unique(array_keys($forum_ary));
+
+ // Determine first forum the user is able to read into - for global announcement link
+ $sql = 'SELECT forum_id
+ FROM ' . FORUMS_TABLE . '
+ WHERE forum_type = ' . FORUM_POST;
+
+ if (sizeof($forum_ary))
+ {
+ $sql .= ' AND ' . $db->sql_in_set('forum_id', $forum_ary, true);
+ }
+ $result = $db->sql_query_limit($sql, 1);
+ $g_forum_id = (int) $db->sql_fetchfield('forum_id');
+ $db->sql_freeresult($result);
+
+ $sql = "SELECT t.* $sql_select
+ FROM $sql_from
+ WHERE t.forum_id = 0
+ AND t.topic_type = " . POST_GLOBAL . '
+ ORDER BY t.topic_last_post_time DESC';
+
+ $topic_list = $rowset = array();
+ // If the user can't see any forums, he can't read any posts because fid of 0 is invalid
+ if ($g_forum_id)
+ {
+ $result = $db->sql_query($sql);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $topic_list[] = $row['topic_id'];
+ $rowset[$row['topic_id']] = $row;
+ }
+ $db->sql_freeresult($result);
+ }
+
+ $topic_tracking_info = array();
+ if ($config['load_db_lastread'])
+ {
+ $topic_tracking_info = get_topic_tracking(0, $topic_list, $rowset, false, $topic_list);
+ }
+ else
+ {
+ $topic_tracking_info = get_complete_topic_tracking(0, $topic_list, $topic_list);
+ }
+
+ foreach ($topic_list as $topic_id)
+ {
+ $row = &$rowset[$topic_id];
+
+ $forum_id = $row['forum_id'];
+ $topic_id = $row['topic_id'];
+
+ $unread_topic = (isset($topic_tracking_info[$topic_id]) && $row['topic_last_post_time'] > $topic_tracking_info[$topic_id]) ? true : false;
+
+ $folder_img = ($unread_topic) ? $folder_new : $folder;
+ $folder_alt = ($unread_topic) ? 'NEW_POSTS' : (($row['topic_status'] == ITEM_LOCKED) ? 'TOPIC_LOCKED' : 'NO_NEW_POSTS');
+
+ if ($row['topic_status'] == ITEM_LOCKED)
+ {
+ $folder_img .= '_locked';
+ }
+
+ // Posted image?
+ if (!empty($row['topic_posted']) && $row['topic_posted'])
+ {
+ $folder_img .= '_mine';
+ }
+
+ $template->assign_block_vars('topicrow', array(
+ 'FORUM_ID' => $forum_id,
+ 'TOPIC_ID' => $topic_id,
+ 'TOPIC_AUTHOR' => get_username_string('username', $row['topic_poster'], $row['topic_first_poster_name'], $row['topic_first_poster_colour']),
+ 'TOPIC_AUTHOR_COLOUR' => get_username_string('colour', $row['topic_poster'], $row['topic_first_poster_name'], $row['topic_first_poster_colour']),
+ 'TOPIC_AUTHOR_FULL' => get_username_string('full', $row['topic_poster'], $row['topic_first_poster_name'], $row['topic_first_poster_colour']),
+ 'FIRST_POST_TIME' => $user->format_date($row['topic_time']),
+ 'LAST_POST_SUBJECT' => censor_text($row['topic_last_post_subject']),
+ 'LAST_POST_TIME' => $user->format_date($row['topic_last_post_time']),
+ 'LAST_VIEW_TIME' => $user->format_date($row['topic_last_view_time']),
+ 'LAST_POST_AUTHOR' => get_username_string('username', $row['topic_last_poster_id'], $row['topic_last_poster_name'], $row['topic_last_poster_colour']),
+ 'LAST_POST_AUTHOR_COLOUR' => get_username_string('colour', $row['topic_last_poster_id'], $row['topic_last_poster_name'], $row['topic_last_poster_colour']),
+ 'LAST_POST_AUTHOR_FULL' => get_username_string('full', $row['topic_last_poster_id'], $row['topic_last_poster_name'], $row['topic_last_poster_colour']),
+ 'TOPIC_TITLE' => censor_text($row['topic_title']),
+ 'TOPIC_TYPE' => $topic_type,
+
+ 'TOPIC_FOLDER_IMG' => $user->img($folder_img, $folder_alt),
+ 'TOPIC_FOLDER_IMG_SRC' => $user->img($folder_img, $folder_alt, false, '', 'src'),
+ 'ATTACH_ICON_IMG' => ($auth->acl_get('u_download') && $auth->acl_get('f_download', $forum_id) && $row['topic_attachment']) ? $user->img('icon_topic_attach', '') : '',
+
+ 'S_USER_POSTED' => (!empty($row['topic_posted']) && $row['topic_posted']) ? true : false,
+ 'S_UNREAD' => $unread_topic,
+
+ 'U_TOPIC_AUTHOR' => get_username_string('profile', $row['topic_poster'], $row['topic_first_poster_name'], $row['topic_first_poster_colour']),
+ 'U_LAST_POST' => append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$g_forum_id&amp;t=$topic_id&amp;p=" . $row['topic_last_post_id']) . '#p' . $row['topic_last_post_id'],
+ 'U_LAST_POST_AUTHOR' => get_username_string('profile', $row['topic_last_poster_id'], $row['topic_last_poster_name'], $row['topic_last_poster_colour']),
+ 'U_NEWEST_POST' => append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$g_forum_id&amp;t=$topic_id&amp;view=unread") . '#unread',
+ 'U_VIEW_TOPIC' => append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$g_forum_id&amp;t=$topic_id"))
+ );
+ }
+
+ if ($config['load_user_activity'])
+ {
+ if (!function_exists('display_user_activity'))
+ {
+ include_once($phpbb_root_path . 'includes/functions_display.' . $phpEx);
+ }
+ display_user_activity($user->data);
+ }
+
+ // Do the relevant calculations
+ $memberdays = max(1, round((time() - $user->data['user_regdate']) / 86400));
+ $posts_per_day = $user->data['user_posts'] / $memberdays;
+ $percentage = ($config['num_posts']) ? min(100, ($user->data['user_posts'] / $config['num_posts']) * 100) : 0;
+
+ $template->assign_vars(array(
+ 'USER_COLOR' => (!empty($user->data['user_colour'])) ? $user->data['user_colour'] : '',
+ 'JOINED' => $user->format_date($user->data['user_regdate']),
+ 'VISITED' => (empty($last_visit)) ? ' - ' : $user->format_date($last_visit),
+ 'WARNINGS' => ($user->data['user_warnings']) ? $user->data['user_warnings'] : 0,
+ 'POSTS' => ($user->data['user_posts']) ? $user->data['user_posts'] : 0,
+ 'POSTS_DAY' => sprintf($user->lang['POST_DAY'], $posts_per_day),
+ 'POSTS_PCT' => sprintf($user->lang['POST_PCT'], $percentage),
+
+ 'OCCUPATION' => (!empty($row['user_occ'])) ? $row['user_occ'] : '',
+ 'INTERESTS' => (!empty($row['user_interests'])) ? $row['user_interests'] : '',
+
+// 'S_GROUP_OPTIONS' => $group_options,
+
+ 'U_SEARCH_USER' => ($auth->acl_get('u_search')) ? append_sid("{$phpbb_root_path}search.$phpEx", 'author_id=' . $user->data['user_id'] . '&amp;sr=posts') : '',
+ ));
+
+ break;
+
+ case 'subscribed':
+
+ include($phpbb_root_path . 'includes/functions_display.' . $phpEx);
+
+ $user->add_lang('viewforum');
+
+ add_form_key('ucp_front_subscribed');
+
+ $unwatch = (isset($_POST['unwatch'])) ? true : false;
+
+ if ($unwatch)
+ {
+ if (check_form_key('ucp_front_subscribed'))
+ {
+ $forums = array_keys(request_var('f', array(0 => 0)));
+ $topics = array_keys(request_var('t', array(0 => 0)));
+ $msg = '';
+
+ if (sizeof($forums) || sizeof($topics))
+ {
+ $l_unwatch = '';
+ if (sizeof($forums))
+ {
+ $sql = 'DELETE FROM ' . FORUMS_WATCH_TABLE . '
+ WHERE ' . $db->sql_in_set('forum_id', $forums) . '
+ AND user_id = ' . $user->data['user_id'];
+ $db->sql_query($sql);
+
+ $l_unwatch .= '_FORUMS';
+ }
+
+ if (sizeof($topics))
+ {
+ $sql = 'DELETE FROM ' . TOPICS_WATCH_TABLE . '
+ WHERE ' . $db->sql_in_set('topic_id', $topics) . '
+ AND user_id = ' . $user->data['user_id'];
+ $db->sql_query($sql);
+
+ $l_unwatch .= '_TOPICS';
+ }
+ $msg = $user->lang['UNWATCHED' . $l_unwatch];
+ }
+ else
+ {
+ $msg = $user->lang['NO_WATCHED_SELECTED'];
+ }
+ }
+ else
+ {
+ $msg = $user->lang['FORM_INVALID'];
+ }
+ $message = $msg . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . append_sid("{$phpbb_root_path}ucp.$phpEx", "i=$id&amp;mode=subscribed") . '">', '</a>');
+ meta_refresh(3, append_sid("{$phpbb_root_path}ucp.$phpEx", "i=$id&amp;mode=subscribed"));
+ trigger_error($message);
+ }
+
+ $forbidden_forums = array();
+
+ if ($config['allow_forum_notify'])
+ {
+ $forbidden_forums = $auth->acl_getf('!f_read', true);
+ $forbidden_forums = array_unique(array_keys($forbidden_forums));
+
+ $sql_array = array(
+ 'SELECT' => 'f.*',
+
+ 'FROM' => array(
+ FORUMS_WATCH_TABLE => 'fw',
+ FORUMS_TABLE => 'f'
+ ),
+
+ 'WHERE' => 'fw.user_id = ' . $user->data['user_id'] . '
+ AND f.forum_id = fw.forum_id
+ AND ' . $db->sql_in_set('f.forum_id', $forbidden_forums, true, true),
+
+ 'ORDER_BY' => 'left_id'
+ );
+
+ if ($config['load_db_lastread'])
+ {
+ $sql_array['LEFT_JOIN'] = array(
+ array(
+ 'FROM' => array(FORUMS_TRACK_TABLE => 'ft'),
+ 'ON' => 'ft.user_id = ' . $user->data['user_id'] . ' AND ft.forum_id = f.forum_id'
+ )
+ );
+
+ $sql_array['SELECT'] .= ', ft.mark_time ';
+ }
+ else
+ {
+ $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
+ $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
+ }
+
+ $sql = $db->sql_build_query('SELECT', $sql_array);
+ $result = $db->sql_query($sql);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $forum_id = $row['forum_id'];
+
+ if ($config['load_db_lastread'])
+ {
+ $forum_check = (!empty($row['mark_time'])) ? $row['mark_time'] : $user->data['user_lastmark'];
+ }
+ else
+ {
+ $forum_check = (isset($tracking_topics['f'][$forum_id])) ? (int) (base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate']) : $user->data['user_lastmark'];
+ }
+
+ $unread_forum = ($row['forum_last_post_time'] > $forum_check) ? true : false;
+
+ // Which folder should we display?
+ if ($row['forum_status'] == ITEM_LOCKED)
+ {
+ $folder_image = ($unread_forum) ? 'forum_unread_locked' : 'forum_read_locked';
+ $folder_alt = 'FORUM_LOCKED';
+ }
+ else
+ {
+ $folder_image = ($unread_forum) ? 'forum_unread' : 'forum_read';
+ $folder_alt = ($unread_forum) ? 'NEW_POSTS' : 'NO_NEW_POSTS';
+ }
+
+ // Create last post link information, if appropriate
+ if ($row['forum_last_post_id'])
+ {
+ $last_post_time = $user->format_date($row['forum_last_post_time']);
+ $last_post_url = append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$forum_id&amp;p=" . $row['forum_last_post_id']) . '#p' . $row['forum_last_post_id'];
+ }
+ else
+ {
+ $last_post_time = $last_post_url = '';
+ }
+
+ $template->assign_block_vars('forumrow', array(
+ 'FORUM_ID' => $forum_id,
+ 'FORUM_FOLDER_IMG' => $user->img($folder_image, $folder_alt),
+ 'FORUM_FOLDER_IMG_SRC' => $user->img($folder_image, $folder_alt, false, '', 'src'),
+ 'FORUM_IMAGE' => ($row['forum_image']) ? '<img src="' . $phpbb_root_path . $row['forum_image'] . '" alt="' . $user->lang[$folder_alt] . '" />' : '',
+ 'FORUM_IMAGE_SRC' => ($row['forum_image']) ? $phpbb_root_path . $row['forum_image'] : '',
+ 'FORUM_NAME' => $row['forum_name'],
+ 'FORUM_DESC' => generate_text_for_display($row['forum_desc'], $row['forum_desc_uid'], $row['forum_desc_bitfield'], $row['forum_desc_options']),
+ 'LAST_POST_SUBJECT' => $row['forum_last_post_subject'],
+ 'LAST_POST_TIME' => $last_post_time,
+
+ 'LAST_POST_AUTHOR' => get_username_string('username', $row['forum_last_poster_id'], $row['forum_last_poster_name'], $row['forum_last_poster_colour']),
+ 'LAST_POST_AUTHOR_COLOUR' => get_username_string('colour', $row['forum_last_poster_id'], $row['forum_last_poster_name'], $row['forum_last_poster_colour']),
+ 'LAST_POST_AUTHOR_FULL' => get_username_string('full', $row['forum_last_poster_id'], $row['forum_last_poster_name'], $row['forum_last_poster_colour']),
+ 'U_LAST_POST_AUTHOR' => get_username_string('profile', $row['forum_last_poster_id'], $row['forum_last_poster_name'], $row['forum_last_poster_colour']),
+
+ 'U_LAST_POST' => $last_post_url,
+ 'U_VIEWFORUM' => append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $row['forum_id']))
+ );
+ }
+ $db->sql_freeresult($result);
+ }
+
+ // Subscribed Topics
+ if ($config['allow_topic_notify'])
+ {
+ if (empty($forbidden_forums))
+ {
+ $forbidden_forums = $auth->acl_getf('!f_read', true);
+ $forbidden_forums = array_unique(array_keys($forbidden_forums));
+ }
+ $this->assign_topiclist('subscribed', $forbidden_forums);
+ }
+
+ $template->assign_vars(array(
+ 'S_TOPIC_NOTIFY' => $config['allow_topic_notify'],
+ 'S_FORUM_NOTIFY' => $config['allow_forum_notify'],
+ ));
+
+ break;
+
+ case 'bookmarks':
+
+ if (!$config['allow_bookmarks'])
+ {
+ $template->assign_vars(array(
+ 'S_NO_DISPLAY_BOOKMARKS' => true)
+ );
+ break;
+ }
+
+ include($phpbb_root_path . 'includes/functions_display.' . $phpEx);
+
+ $user->add_lang('viewforum');
+
+ if (isset($_POST['unbookmark']))
+ {
+ $s_hidden_fields = array('unbookmark' => 1);
+ $topics = (isset($_POST['t'])) ? array_keys(request_var('t', array(0 => 0))) : array();
+ $url = $this->u_action;
+
+ if (!sizeof($topics))
+ {
+ trigger_error('NO_BOOKMARKS_SELECTED');
+ }
+
+ foreach ($topics as $topic_id)
+ {
+ $s_hidden_fields['t'][$topic_id] = 1;
+ }
+
+ if (confirm_box(true))
+ {
+ $sql = 'DELETE FROM ' . BOOKMARKS_TABLE . '
+ WHERE user_id = ' . $user->data['user_id'] . '
+ AND ' . $db->sql_in_set('topic_id', $topics);
+ $db->sql_query($sql);
+
+ meta_refresh(3, $url);
+ $message = $user->lang['BOOKMARKS_REMOVED'] . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $url . '">', '</a>');
+ trigger_error($message);
+ }
+ else
+ {
+ confirm_box(false, 'REMOVE_SELECTED_BOOKMARKS', build_hidden_fields($s_hidden_fields));
+ }
+ }
+ $forbidden_forums = $auth->acl_getf('!f_read', true);
+ $forbidden_forums = array_unique(array_keys($forbidden_forums));
+
+ $this->assign_topiclist('bookmarks', $forbidden_forums);
+
+ break;
+
+ case 'drafts':
+
+ $pm_drafts = ($this->p_master->p_name == 'pm') ? true : false;
+ $template->assign_var('S_SHOW_DRAFTS', true);
+
+ $user->add_lang('posting');
+
+ $edit = (isset($_REQUEST['edit'])) ? true : false;
+ $submit = (isset($_POST['submit'])) ? true : false;
+ $draft_id = ($edit) ? intval($_REQUEST['edit']) : 0;
+ $delete = (isset($_POST['delete'])) ? true : false;
+
+ $s_hidden_fields = ($edit) ? '<input type="hidden" name="edit" value="' . $draft_id . '" />' : '';
+ $draft_subject = $draft_message = '';
+ add_form_key('ucp_draft');
+
+ if ($delete)
+ {
+ if (check_form_key('ucp_draft'))
+ {
+ $drafts = array_keys(request_var('d', array(0 => 0)));
+
+ if (sizeof($drafts))
+ {
+ $sql = 'DELETE FROM ' . DRAFTS_TABLE . '
+ WHERE ' . $db->sql_in_set('draft_id', $drafts) . '
+ AND user_id = ' . $user->data['user_id'];
+ $db->sql_query($sql);
+ }
+ $msg = $user->lang['DRAFTS_DELETED'];
+ unset($drafts);
+ }
+ else
+ {
+ $msg = $user->lang['FORM_INVALID'];
+ }
+ $message = $msg . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $this->u_action . '">', '</a>');
+ meta_refresh(3, $this->u_action);
+ trigger_error($message);
+ }
+
+ if ($submit && $edit)
+ {
+ $draft_subject = utf8_normalize_nfc(request_var('subject', '', true));
+ $draft_message = utf8_normalize_nfc(request_var('message', '', true));
+ if (check_form_key('ucp_draft'))
+ {
+ if ($draft_message && $draft_subject)
+ {
+ $draft_row = array(
+ 'draft_subject' => $draft_subject,
+ 'draft_message' => $draft_message
+ );
+
+ $sql = 'UPDATE ' . DRAFTS_TABLE . '
+ SET ' . $db->sql_build_array('UPDATE', $draft_row) . "
+ WHERE draft_id = $draft_id
+ AND user_id = " . $user->data['user_id'];
+ $db->sql_query($sql);
+
+ $message = $user->lang['DRAFT_UPDATED'] . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $this->u_action . '">', '</a>');
+
+ meta_refresh(3, $this->u_action);
+ trigger_error($message);
+ }
+ else
+ {
+ $template->assign_var('ERROR', ($draft_message == '') ? $user->lang['EMPTY_DRAFT'] : (($draft_subject == '') ? $user->lang['EMPTY_DRAFT_TITLE'] : ''));
+ }
+ }
+ else
+ {
+ $template->assign_var('ERROR', $user->lang['FORM_INVALID']);
+ }
+ }
+
+ if (!$pm_drafts)
+ {
+ $sql = 'SELECT d.*, f.forum_name
+ FROM ' . DRAFTS_TABLE . ' d, ' . FORUMS_TABLE . ' f
+ WHERE d.user_id = ' . $user->data['user_id'] . ' ' .
+ (($edit) ? "AND d.draft_id = $draft_id" : '') . '
+ AND f.forum_id = d.forum_id
+ ORDER BY d.save_time DESC';
+ }
+ else
+ {
+ $sql = 'SELECT * FROM ' . DRAFTS_TABLE . '
+ WHERE user_id = ' . $user->data['user_id'] . ' ' .
+ (($edit) ? "AND draft_id = $draft_id" : '') . '
+ AND forum_id = 0
+ AND topic_id = 0
+ ORDER BY save_time DESC';
+ }
+ $result = $db->sql_query($sql);
+
+ $draftrows = $topic_ids = array();
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ if ($row['topic_id'])
+ {
+ $topic_ids[] = (int) $row['topic_id'];
+ }
+ $draftrows[] = $row;
+ }
+ $db->sql_freeresult($result);
+
+ if (sizeof($topic_ids))
+ {
+ $sql = 'SELECT topic_id, forum_id, topic_title
+ FROM ' . TOPICS_TABLE . '
+ WHERE ' . $db->sql_in_set('topic_id', array_unique($topic_ids));
+ $result = $db->sql_query($sql);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $topic_rows[$row['topic_id']] = $row;
+ }
+ $db->sql_freeresult($result);
+ }
+ unset($topic_ids);
+
+ $template->assign_var('S_EDIT_DRAFT', $edit);
+
+ $row_count = 0;
+ foreach ($draftrows as $draft)
+ {
+ $link_topic = $link_forum = $link_pm = false;
+ $insert_url = $view_url = $title = '';
+
+ if (isset($topic_rows[$draft['topic_id']]) && $auth->acl_get('f_read', $topic_rows[$draft['topic_id']]['forum_id']))
+ {
+ $link_topic = true;
+ $view_url = append_sid("{$phpbb_root_path}viewtopic.$phpEx", 'f=' . $topic_rows[$draft['topic_id']]['forum_id'] . '&amp;t=' . $draft['topic_id']);
+ $title = $topic_rows[$draft['topic_id']]['topic_title'];
+
+ $insert_url = append_sid("{$phpbb_root_path}posting.$phpEx", 'f=' . $topic_rows[$draft['topic_id']]['forum_id'] . '&amp;t=' . $draft['topic_id'] . '&amp;mode=reply&amp;d=' . $draft['draft_id']);
+ }
+ else if ($auth->acl_get('f_read', $draft['forum_id']))
+ {
+ $link_forum = true;
+ $view_url = append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $draft['forum_id']);
+ $title = $draft['forum_name'];
+
+ $insert_url = append_sid("{$phpbb_root_path}posting.$phpEx", 'f=' . $draft['forum_id'] . '&amp;mode=post&amp;d=' . $draft['draft_id']);
+ }
+ else if ($pm_drafts)
+ {
+ $link_pm = true;
+ $insert_url = append_sid("{$phpbb_root_path}ucp.$phpEx", "i=$id&amp;mode=compose&amp;d=" . $draft['draft_id']);
+ }
+
+ $template_row = array(
+ 'DATE' => $user->format_date($draft['save_time']),
+ 'DRAFT_MESSAGE' => ($submit) ? $draft_message : $draft['draft_message'],
+ 'DRAFT_SUBJECT' => ($submit) ? $draft_subject : $draft['draft_subject'],
+ 'TITLE' => $title,
+
+ 'DRAFT_ID' => $draft['draft_id'],
+ 'FORUM_ID' => $draft['forum_id'],
+ 'TOPIC_ID' => $draft['topic_id'],
+
+ 'U_VIEW' => $view_url,
+ 'U_VIEW_EDIT' => $this->u_action . '&amp;edit=' . $draft['draft_id'],
+ 'U_INSERT' => $insert_url,
+
+ 'S_LINK_TOPIC' => $link_topic,
+ 'S_LINK_FORUM' => $link_forum,
+ 'S_LINK_PM' => $link_pm,
+ 'S_HIDDEN_FIELDS' => $s_hidden_fields
+ );
+ $row_count++;
+
+ ($edit) ? $template->assign_vars($template_row) : $template->assign_block_vars('draftrow', $template_row);
+ }
+
+ if (!$edit)
+ {
+ $template->assign_var('S_DRAFT_ROWS', $row_count);
+ }
+
+ break;
+ }
+
+
+ $template->assign_vars(array(
+ 'L_TITLE' => $user->lang['UCP_MAIN_' . strtoupper($mode)],
+
+ 'S_DISPLAY_MARK_ALL' => ($mode == 'watched' || ($mode == 'drafts' && !isset($_GET['edit']))) ? true : false,
+ 'S_HIDDEN_FIELDS' => (isset($s_hidden_fields)) ? $s_hidden_fields : '',
+ 'S_UCP_ACTION' => $this->u_action,
+
+ 'LAST_POST_IMG' => $user->img('icon_topic_latest', 'VIEW_LATEST_POST'),
+ 'NEWEST_POST_IMG' => $user->img('icon_topic_newest', 'VIEW_NEWEST_POST'),
+ ));
+
+ // Set desired template
+ $this->tpl_name = 'ucp_main_' . $mode;
+ $this->page_title = 'UCP_MAIN_' . strtoupper($mode);
+ }
+
+ /**
+ * Build and assign topiclist for bookmarks/subscribed topics
+ */
+ function assign_topiclist($mode = 'subscribed', $forbidden_forum_ary = array())
+ {
+ global $user, $db, $template, $config, $cache, $auth, $phpbb_root_path, $phpEx;
+
+ $table = ($mode == 'subscribed') ? TOPICS_WATCH_TABLE : BOOKMARKS_TABLE;
+ $start = request_var('start', 0);
+
+ // Grab icons
+ $icons = $cache->obtain_icons();
+
+ $sql_array = array(
+ 'SELECT' => 'COUNT(t.topic_id) as topics_count',
+
+ 'FROM' => array(
+ $table => 'i',
+ TOPICS_TABLE => 't'
+ ),
+
+ 'WHERE' => 'i.topic_id = t.topic_id
+ AND i.user_id = ' . $user->data['user_id'] . '
+ AND ' . $db->sql_in_set('t.forum_id', $forbidden_forum_ary, true, true),
+ );
+ $sql = $db->sql_build_query('SELECT', $sql_array);
+ $result = $db->sql_query($sql);
+ $topics_count = (int) $db->sql_fetchfield('topics_count');
+ $db->sql_freeresult($result);
+
+ if ($topics_count)
+ {
+ $template->assign_vars(array(
+ 'PAGINATION' => generate_pagination($this->u_action, $topics_count, $config['topics_per_page'], $start),
+ 'PAGE_NUMBER' => on_page($topics_count, $config['topics_per_page'], $start),
+ 'TOTAL_TOPICS' => ($topics_count == 1) ? $user->lang['VIEW_FORUM_TOPIC'] : sprintf($user->lang['VIEW_FORUM_TOPICS'], $topics_count))
+ );
+ }
+
+ if ($mode == 'subscribed')
+ {
+ $sql_array = array(
+ 'SELECT' => 't.*, f.forum_name',
+
+ 'FROM' => array(
+ TOPICS_WATCH_TABLE => 'tw',
+ TOPICS_TABLE => 't'
+ ),
+
+ 'WHERE' => 'tw.user_id = ' . $user->data['user_id'] . '
+ AND t.topic_id = tw.topic_id
+ AND ' . $db->sql_in_set('t.forum_id', $forbidden_forum_ary, true, true),
+
+
+ 'ORDER_BY' => 't.topic_last_post_time DESC'
+ );
+
+ $sql_array['LEFT_JOIN'] = array();
+ }
+ else
+ {
+ $sql_array = array(
+ 'SELECT' => 't.*, f.forum_name, b.topic_id as b_topic_id',
+
+ 'FROM' => array(
+ BOOKMARKS_TABLE => 'b',
+ ),
+
+ 'WHERE' => 'b.user_id = ' . $user->data['user_id'] . '
+ AND ' . $db->sql_in_set('f.forum_id', $forbidden_forum_ary, true, true),
+
+ 'ORDER_BY' => 't.topic_last_post_time DESC'
+ );
+
+ $sql_array['LEFT_JOIN'] = array();
+ $sql_array['LEFT_JOIN'][] = array('FROM' => array(TOPICS_TABLE => 't'), 'ON' => 'b.topic_id = t.topic_id');
+ }
+
+ $sql_array['LEFT_JOIN'][] = array('FROM' => array(FORUMS_TABLE => 'f'), 'ON' => 't.forum_id = f.forum_id');
+
+ if ($config['load_db_lastread'])
+ {
+ $sql_array['LEFT_JOIN'][] = array('FROM' => array(FORUMS_TRACK_TABLE => 'ft'), 'ON' => 'ft.forum_id = t.forum_id AND ft.user_id = ' . $user->data['user_id']);
+ $sql_array['LEFT_JOIN'][] = array('FROM' => array(TOPICS_TRACK_TABLE => 'tt'), 'ON' => 'tt.topic_id = t.topic_id AND tt.user_id = ' . $user->data['user_id']);
+ $sql_array['SELECT'] .= ', tt.mark_time, ft.mark_time AS forum_mark_time';
+ }
+
+ if ($config['load_db_track'])
+ {
+ $sql_array['LEFT_JOIN'][] = array('FROM' => array(TOPICS_POSTED_TABLE => 'tp'), 'ON' => 'tp.topic_id = t.topic_id AND tp.user_id = ' . $user->data['user_id']);
+ $sql_array['SELECT'] .= ', tp.topic_posted';
+ }
+
+ $sql = $db->sql_build_query('SELECT', $sql_array);
+ $result = $db->sql_query_limit($sql, $config['topics_per_page'], $start);
+
+ $topic_list = $topic_forum_list = $global_announce_list = $rowset = array();
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $topic_id = (isset($row['b_topic_id'])) ? $row['b_topic_id'] : $row['topic_id'];
+
+ $topic_list[] = $topic_id;
+ $rowset[$topic_id] = $row;
+
+ $topic_forum_list[$row['forum_id']]['forum_mark_time'] = ($config['load_db_lastread']) ? $row['forum_mark_time'] : 0;
+ $topic_forum_list[$row['forum_id']]['topics'][] = $topic_id;
+
+ if ($row['topic_type'] == POST_GLOBAL)
+ {
+ $global_announce_list[] = $topic_id;
+ }
+ }
+ $db->sql_freeresult($result);
+
+ $topic_tracking_info = array();
+ if ($config['load_db_lastread'])
+ {
+ foreach ($topic_forum_list as $f_id => $topic_row)
+ {
+ $topic_tracking_info += get_topic_tracking($f_id, $topic_row['topics'], $rowset, array($f_id => $topic_row['forum_mark_time']), ($f_id == 0) ? $global_announce_list : false);
+ }
+ }
+ else
+ {
+ foreach ($topic_forum_list as $f_id => $topic_row)
+ {
+ $topic_tracking_info += get_complete_topic_tracking($f_id, $topic_row['topics'], $global_announce_list);
+ }
+ }
+
+ foreach ($topic_list as $topic_id)
+ {
+ $row = &$rowset[$topic_id];
+
+ $forum_id = $row['forum_id'];
+ $topic_id = (isset($row['b_topic_id'])) ? $row['b_topic_id'] : $row['topic_id'];
+
+ $unread_topic = (isset($topic_tracking_info[$topic_id]) && $row['topic_last_post_time'] > $topic_tracking_info[$topic_id]) ? true : false;
+
+ // Replies
+ $replies = ($auth->acl_get('m_approve', $forum_id)) ? $row['topic_replies_real'] : $row['topic_replies'];
+
+ if ($row['topic_status'] == ITEM_MOVED && !empty($row['topic_moved_id']))
+ {
+ $topic_id = $row['topic_moved_id'];
+ }
+
+ // Get folder img, topic status/type related information
+ $folder_img = $folder_alt = $topic_type = '';
+ topic_status($row, $replies, $unread_topic, $folder_img, $folder_alt, $topic_type);
+
+ $view_topic_url_params = "f=$forum_id&amp;t=$topic_id";
+ $view_topic_url = append_sid("{$phpbb_root_path}viewtopic.$phpEx", $view_topic_url_params);
+
+ // Send vars to template
+ $template->assign_block_vars('topicrow', array(
+ 'FORUM_ID' => $forum_id,
+ 'TOPIC_ID' => $topic_id,
+ 'FIRST_POST_TIME' => $user->format_date($row['topic_time']),
+ 'LAST_POST_SUBJECT' => $row['topic_last_post_subject'],
+ 'LAST_POST_TIME' => $user->format_date($row['topic_last_post_time']),
+ 'LAST_VIEW_TIME' => $user->format_date($row['topic_last_view_time']),
+
+ 'TOPIC_AUTHOR' => get_username_string('username', $row['topic_poster'], $row['topic_first_poster_name'], $row['topic_first_poster_colour']),
+ 'TOPIC_AUTHOR_COLOUR' => get_username_string('colour', $row['topic_poster'], $row['topic_first_poster_name'], $row['topic_first_poster_colour']),
+ 'TOPIC_AUTHOR_FULL' => get_username_string('full', $row['topic_poster'], $row['topic_first_poster_name'], $row['topic_first_poster_colour']),
+ 'U_TOPIC_AUTHOR' => get_username_string('profile', $row['topic_poster'], $row['topic_first_poster_name'], $row['topic_first_poster_colour']),
+
+ 'LAST_POST_AUTHOR' => get_username_string('username', $row['topic_last_poster_id'], $row['topic_last_poster_name'], $row['topic_last_poster_colour']),
+ 'LAST_POST_AUTHOR_COLOUR' => get_username_string('colour', $row['topic_last_poster_id'], $row['topic_last_poster_name'], $row['topic_last_poster_colour']),
+ 'LAST_POST_AUTHOR_FULL' => get_username_string('full', $row['topic_last_poster_id'], $row['topic_last_poster_name'], $row['topic_last_poster_colour']),
+ 'U_LAST_POST_AUTHOR' => get_username_string('profile', $row['topic_last_poster_id'], $row['topic_last_poster_name'], $row['topic_last_poster_colour']),
+
+ 'S_DELETED_TOPIC' => (!$row['topic_id']) ? true : false,
+ 'S_GLOBAL_TOPIC' => (!$forum_id) ? true : false,
+
+ 'PAGINATION' => topic_generate_pagination($replies, append_sid("{$phpbb_root_path}viewtopic.$phpEx", 'f=' . (($row['forum_id']) ? $row['forum_id'] : $forum_id) . "&amp;t=$topic_id")),
+ 'REPLIES' => $replies,
+ 'VIEWS' => $row['topic_views'],
+ 'TOPIC_TITLE' => censor_text($row['topic_title']),
+ 'TOPIC_TYPE' => $topic_type,
+ 'FORUM_NAME' => $row['forum_name'],
+
+ 'TOPIC_FOLDER_IMG' => $user->img($folder_img, $folder_alt),
+ 'TOPIC_FOLDER_IMG_SRC' => $user->img($folder_img, $folder_alt, false, '', 'src'),
+ 'TOPIC_FOLDER_IMG_ALT' => $user->lang[$folder_alt],
+ 'TOPIC_ICON_IMG' => (!empty($icons[$row['icon_id']])) ? $icons[$row['icon_id']]['img'] : '',
+ 'TOPIC_ICON_IMG_WIDTH' => (!empty($icons[$row['icon_id']])) ? $icons[$row['icon_id']]['width'] : '',
+ 'TOPIC_ICON_IMG_HEIGHT' => (!empty($icons[$row['icon_id']])) ? $icons[$row['icon_id']]['height'] : '',
+ 'ATTACH_ICON_IMG' => ($auth->acl_get('u_download') && $auth->acl_get('f_download', $forum_id) && $row['topic_attachment']) ? $user->img('icon_topic_attach', $user->lang['TOTAL_ATTACHMENTS']) : '',
+
+ 'S_TOPIC_TYPE' => $row['topic_type'],
+ 'S_USER_POSTED' => (!empty($row['topic_posted'])) ? true : false,
+ 'S_UNREAD_TOPIC' => $unread_topic,
+
+ 'U_NEWEST_POST' => append_sid("{$phpbb_root_path}viewtopic.$phpEx", $view_topic_url_params . '&amp;view=unread') . '#unread',
+ 'U_LAST_POST' => append_sid("{$phpbb_root_path}viewtopic.$phpEx", $view_topic_url_params . '&amp;p=' . $row['topic_last_post_id']) . '#p' . $row['topic_last_post_id'],
+ 'U_VIEW_TOPIC' => $view_topic_url,
+ 'U_VIEW_FORUM' => append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $forum_id),
+ ));
+ }
+ }
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/ucp_pm.php b/phpBB/includes/ucp/ucp_pm.php
new file mode 100644
index 0000000000..e1c51170db
--- /dev/null
+++ b/phpBB/includes/ucp/ucp_pm.php
@@ -0,0 +1,416 @@
+<?php
+/**
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* Private Message Class
+*
+* $_REQUEST['folder'] display folder with the id used
+* $_REQUEST['folder'] inbox|outbox|sentbox display folder with the associated name
+*
+* Display Messages (default to inbox) - mode=view
+* Display single message - mode=view&p=[msg_id] or &p=[msg_id] (short linkage)
+*
+* if the folder id with (&f=[folder_id]) is used when displaying messages, one query will be saved. If it is not used, phpBB needs to grab
+* the folder id first in order to display the input boxes and folder names and such things. ;) phpBB always checks this against the database to make
+* sure the user is able to view the message.
+*
+* Composing Messages (mode=compose):
+* To specific user (u=[user_id])
+* To specific group (g=[group_id])
+* Quoting a post (action=quotepost&p=[post_id])
+* Quoting a PM (action=quote&p=[msg_id])
+* Forwarding a PM (action=forward&p=[msg_id])
+*
+* @package ucp
+*/
+class ucp_pm
+{
+ var $u_action;
+
+ function main($id, $mode)
+ {
+ global $user, $template, $phpbb_root_path, $auth, $phpEx, $db, $config;
+
+ if (!$user->data['is_registered'])
+ {
+ trigger_error('NO_MESSAGE');
+ }
+
+ // Is PM disabled?
+ if (!$config['allow_privmsg'])
+ {
+ trigger_error('PM_DISABLED');
+ }
+
+ $user->add_lang('posting');
+ $template->assign_var('S_PRIVMSGS', true);
+
+ // Folder directly specified?
+ $folder_specified = request_var('folder', '');
+
+ if (!in_array($folder_specified, array('inbox', 'outbox', 'sentbox')))
+ {
+ $folder_specified = (int) $folder_specified;
+ }
+ else
+ {
+ $folder_specified = ($folder_specified == 'inbox') ? PRIVMSGS_INBOX : (($folder_specified == 'outbox') ? PRIVMSGS_OUTBOX : PRIVMSGS_SENTBOX);
+ }
+
+ if (!$folder_specified)
+ {
+ $mode = (!$mode) ? request_var('mode', 'view') : $mode;
+ }
+ else
+ {
+ $mode = 'view';
+ }
+
+ include($phpbb_root_path . 'includes/functions_privmsgs.' . $phpEx);
+
+ switch ($mode)
+ {
+ // New private messages popup
+ case 'popup':
+
+ $l_new_message = '';
+ if ($user->data['is_registered'])
+ {
+ if ($user->data['user_new_privmsg'])
+ {
+ $l_new_message = ($user->data['user_new_privmsg'] == 1) ? $user->lang['YOU_NEW_PM'] : $user->lang['YOU_NEW_PMS'];
+ }
+ else
+ {
+ $l_new_message = $user->lang['YOU_NO_NEW_PM'];
+ }
+ }
+
+ $template->assign_vars(array(
+ 'MESSAGE' => $l_new_message,
+ 'S_NOT_LOGGED_IN' => ($user->data['user_id'] == ANONYMOUS) ? true : false,
+ 'CLICK_TO_VIEW' => sprintf($user->lang['CLICK_VIEW_PRIVMSG'], '<a href="' . append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;folder=inbox') . '" onclick="jump_to_inbox(this.href); return false;">', '</a>'),
+ 'U_INBOX' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;folder=inbox'),
+ 'UA_INBOX' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&folder=inbox', false))
+ );
+
+ $tpl_file = 'ucp_pm_popup';
+ break;
+
+ // Compose message
+ case 'compose':
+ $action = request_var('action', 'post');
+
+ get_folder($user->data['user_id']);
+
+ if (!$auth->acl_get('u_sendpm'))
+ {
+ // trigger_error('NO_AUTH_SEND_MESSAGE');
+ $template->assign_vars(array(
+ 'S_NO_AUTH_SEND_MESSAGE' => true,
+ 'S_COMPOSE_PM_VIEW' => true,
+ ));
+
+ $tpl_file = 'ucp_pm_viewfolder';
+ break;
+ }
+
+ include($phpbb_root_path . 'includes/ucp/ucp_pm_compose.' . $phpEx);
+ compose_pm($id, $mode, $action);
+
+ $tpl_file = 'posting_body';
+ break;
+
+ case 'options':
+ set_user_message_limit();
+ get_folder($user->data['user_id']);
+
+ include($phpbb_root_path . 'includes/ucp/ucp_pm_options.' . $phpEx);
+ message_options($id, $mode, $global_privmsgs_rules, $global_rule_conditions);
+
+ $tpl_file = 'ucp_pm_options';
+ break;
+
+ case 'drafts':
+
+ get_folder($user->data['user_id']);
+ $this->p_name = 'pm';
+
+ // Call another module... please do not try this at home... Hoochie Coochie Man
+ include($phpbb_root_path . 'includes/ucp/ucp_main.' . $phpEx);
+
+ $module = new ucp_main($this);
+ $module->u_action = $this->u_action;
+ $module->main($id, $mode);
+
+ $this->tpl_name = $module->tpl_name;
+ $this->page_title = 'UCP_PM_DRAFTS';
+
+ unset($module);
+ return;
+
+ break;
+
+ case 'view':
+
+ set_user_message_limit();
+
+ if ($folder_specified)
+ {
+ $folder_id = $folder_specified;
+ $action = 'view_folder';
+ }
+ else
+ {
+ $folder_id = request_var('f', PRIVMSGS_NO_BOX);
+ $action = request_var('action', 'view_folder');
+ }
+
+ $msg_id = request_var('p', 0);
+ $view = request_var('view', '');
+
+ // View message if specified
+ if ($msg_id)
+ {
+ $action = 'view_message';
+ }
+
+ if (!$auth->acl_get('u_readpm'))
+ {
+ trigger_error('NO_AUTH_READ_MESSAGE');
+ }
+
+ // Do not allow hold messages to be seen
+ if ($folder_id == PRIVMSGS_HOLD_BOX)
+ {
+ trigger_error('NO_AUTH_READ_HOLD_MESSAGE');
+ }
+
+
+ // First Handle Mark actions and moving messages
+ $submit_mark = (isset($_POST['submit_mark'])) ? true : false;
+ $move_pm = (isset($_POST['move_pm'])) ? true : false;
+ $mark_option = request_var('mark_option', '');
+ $dest_folder = request_var('dest_folder', PRIVMSGS_NO_BOX);
+
+ // Is moving PM triggered through mark options?
+ if (!in_array($mark_option, array('mark_important', 'delete_marked')) && $submit_mark)
+ {
+ $move_pm = true;
+ $dest_folder = (int) $mark_option;
+ $submit_mark = false;
+ }
+
+ // Move PM
+ if ($move_pm)
+ {
+ $move_msg_ids = (isset($_POST['marked_msg_id'])) ? request_var('marked_msg_id', array(0)) : array();
+ $cur_folder_id = request_var('cur_folder_id', PRIVMSGS_NO_BOX);
+
+ if (move_pm($user->data['user_id'], $user->data['message_limit'], $move_msg_ids, $dest_folder, $cur_folder_id))
+ {
+ // Return to folder view if single message moved
+ if ($action == 'view_message')
+ {
+ $msg_id = 0;
+ $folder_id = request_var('cur_folder_id', PRIVMSGS_NO_BOX);
+ $action = 'view_folder';
+ }
+ }
+ }
+
+ // Message Mark Options
+ if ($submit_mark)
+ {
+ handle_mark_actions($user->data['user_id'], $mark_option);
+ }
+
+ // If new messages arrived, place them into the appropriate folder
+ $num_not_moved = $num_removed = 0;
+ $release = request_var('release', 0);
+
+ if ($user->data['user_new_privmsg'] && $action == 'view_folder')
+ {
+ $return = place_pm_into_folder($global_privmsgs_rules, $release);
+ $num_not_moved = $return['not_moved'];
+ $num_removed = $return['removed'];
+ }
+
+ if (!$msg_id && $folder_id == PRIVMSGS_NO_BOX)
+ {
+ $folder_id = PRIVMSGS_INBOX;
+ }
+ else if ($msg_id && $folder_id == PRIVMSGS_NO_BOX)
+ {
+ $sql = 'SELECT folder_id
+ FROM ' . PRIVMSGS_TO_TABLE . "
+ WHERE msg_id = $msg_id
+ AND folder_id <> " . PRIVMSGS_NO_BOX . '
+ AND user_id = ' . $user->data['user_id'];
+ $result = $db->sql_query($sql);
+ $row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if (!$row)
+ {
+ trigger_error('NO_MESSAGE');
+ }
+ $folder_id = (int) $row['folder_id'];
+ }
+
+ $message_row = array();
+ if ($action == 'view_message' && $msg_id)
+ {
+ // Get Message user want to see
+ if ($view == 'next' || $view == 'previous')
+ {
+ $sql_condition = ($view == 'next') ? '>' : '<';
+ $sql_ordering = ($view == 'next') ? 'ASC' : 'DESC';
+
+ $sql = 'SELECT t.msg_id
+ FROM ' . PRIVMSGS_TO_TABLE . ' t, ' . PRIVMSGS_TABLE . ' p, ' . PRIVMSGS_TABLE . " p2
+ WHERE p2.msg_id = $msg_id
+ AND t.folder_id = $folder_id
+ AND t.user_id = " . $user->data['user_id'] . "
+ AND t.msg_id = p.msg_id
+ AND p.message_time $sql_condition p2.message_time
+ ORDER BY p.message_time $sql_ordering";
+ $result = $db->sql_query_limit($sql, 1);
+ $row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if (!$row)
+ {
+ $message = ($view == 'next') ? 'NO_NEWER_PM' : 'NO_OLDER_PM';
+ trigger_error($message);
+ }
+ else
+ {
+ $msg_id = $row['msg_id'];
+ }
+ }
+
+ $sql = 'SELECT t.*, p.*, u.*
+ FROM ' . PRIVMSGS_TO_TABLE . ' t, ' . PRIVMSGS_TABLE . ' p, ' . USERS_TABLE . ' u
+ WHERE t.user_id = ' . $user->data['user_id'] . "
+ AND p.author_id = u.user_id
+ AND t.folder_id = $folder_id
+ AND t.msg_id = p.msg_id
+ AND p.msg_id = $msg_id";
+ $result = $db->sql_query($sql);
+ $message_row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if (!$message_row)
+ {
+ trigger_error('NO_MESSAGE');
+ }
+
+ // Update unread status
+ update_unread_status($message_row['pm_unread'], $message_row['msg_id'], $user->data['user_id'], $folder_id);
+ }
+
+ $folder = get_folder($user->data['user_id'], $folder_id);
+
+ $s_folder_options = $s_to_folder_options = '';
+ foreach ($folder as $f_id => $folder_ary)
+ {
+ $option = '<option' . ((!in_array($f_id, array(PRIVMSGS_INBOX, PRIVMSGS_OUTBOX, PRIVMSGS_SENTBOX))) ? ' class="sep"' : '') . ' value="' . $f_id . '"' . (($f_id == $folder_id) ? ' selected="selected"' : '') . '>' . $folder_ary['folder_name'] . (($folder_ary['unread_messages']) ? ' [' . $folder_ary['unread_messages'] . '] ' : '') . '</option>';
+
+ $s_to_folder_options .= ($f_id != PRIVMSGS_OUTBOX && $f_id != PRIVMSGS_SENTBOX) ? $option : '';
+ $s_folder_options .= $option;
+ }
+ clean_sentbox($folder[PRIVMSGS_SENTBOX]['num_messages']);
+
+ // Header for message view - folder and so on
+ $folder_status = get_folder_status($folder_id, $folder);
+
+ $template->assign_vars(array(
+ 'CUR_FOLDER_ID' => $folder_id,
+ 'CUR_FOLDER_NAME' => $folder_status['folder_name'],
+ 'NUM_NOT_MOVED' => $num_not_moved,
+ 'NUM_REMOVED' => $num_removed,
+ 'RELEASE_MESSAGE_INFO' => sprintf($user->lang['RELEASE_MESSAGES'], '<a href="' . $this->u_action . '&amp;folder=' . $folder_id . '&amp;release=1">', '</a>'),
+ 'NOT_MOVED_MESSAGES' => ($num_not_moved == 1) ? $user->lang['NOT_MOVED_MESSAGE'] : sprintf($user->lang['NOT_MOVED_MESSAGES'], $num_not_moved),
+ 'RULE_REMOVED_MESSAGES' => ($num_removed == 1) ? $user->lang['RULE_REMOVED_MESSAGE'] : sprintf($user->lang['RULE_REMOVED_MESSAGES'], $num_removed),
+
+ 'S_FOLDER_OPTIONS' => $s_folder_options,
+ 'S_TO_FOLDER_OPTIONS' => $s_to_folder_options,
+ 'S_FOLDER_ACTION' => $this->u_action . '&amp;action=view_folder',
+ 'S_PM_ACTION' => $this->u_action . '&amp;action=' . $action,
+
+ 'U_INBOX' => $this->u_action . '&amp;folder=inbox',
+ 'U_OUTBOX' => $this->u_action . '&amp;folder=outbox',
+ 'U_SENTBOX' => $this->u_action . '&amp;folder=sentbox',
+ 'U_CREATE_FOLDER' => $this->u_action . '&amp;mode=options',
+ 'U_CURRENT_FOLDER' => $this->u_action . '&amp;folder=' . $folder_id,
+
+ 'S_IN_INBOX' => ($folder_id == PRIVMSGS_INBOX) ? true : false,
+ 'S_IN_OUTBOX' => ($folder_id == PRIVMSGS_OUTBOX) ? true : false,
+ 'S_IN_SENTBOX' => ($folder_id == PRIVMSGS_SENTBOX) ? true : false,
+
+ 'FOLDER_STATUS' => $folder_status['message'],
+ 'FOLDER_MAX_MESSAGES' => $folder_status['max'],
+ 'FOLDER_CUR_MESSAGES' => $folder_status['cur'],
+ 'FOLDER_REMAINING_MESSAGES' => $folder_status['remaining'],
+ 'FOLDER_PERCENT' => $folder_status['percent'])
+ );
+
+ if ($action == 'view_folder')
+ {
+ include($phpbb_root_path . 'includes/ucp/ucp_pm_viewfolder.' . $phpEx);
+ view_folder($id, $mode, $folder_id, $folder);
+
+ $tpl_file = 'ucp_pm_viewfolder';
+ }
+ else if ($action == 'view_message')
+ {
+ $template->assign_vars(array(
+ 'S_VIEW_MESSAGE' => true,
+ 'MSG_ID' => $msg_id)
+ );
+
+ if (!$msg_id)
+ {
+ trigger_error('NO_MESSAGE');
+ }
+
+ include($phpbb_root_path . 'includes/ucp/ucp_pm_viewmessage.' . $phpEx);
+ view_message($id, $mode, $folder_id, $msg_id, $folder, $message_row);
+
+ $tpl_file = ($view == 'print') ? 'ucp_pm_viewmessage_print' : 'ucp_pm_viewmessage';
+ }
+
+ break;
+
+ default:
+ trigger_error('NO_ACTION_MODE', E_USER_ERROR);
+ break;
+ }
+
+ $template->assign_vars(array(
+ 'L_TITLE' => $user->lang['UCP_PM_' . strtoupper($mode)],
+ 'S_UCP_ACTION' => $this->u_action . ((isset($action)) ? "&amp;action=$action" : ''))
+ );
+
+ // Set desired template
+ $this->tpl_name = $tpl_file;
+ $this->page_title = 'UCP_PM_' . strtoupper($mode);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/ucp_pm_compose.php b/phpBB/includes/ucp/ucp_pm_compose.php
new file mode 100644
index 0000000000..2f56bcdf7a
--- /dev/null
+++ b/phpBB/includes/ucp/ucp_pm_compose.php
@@ -0,0 +1,1290 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* Compose private message
+* Called from ucp_pm with mode == 'compose'
+*/
+function compose_pm($id, $mode, $action)
+{
+ global $template, $db, $auth, $user;
+ global $phpbb_root_path, $phpEx, $config;
+
+ // Damn php and globals - i know, this is horrible
+ // Needed for handle_message_list_actions()
+ global $refresh, $submit, $preview;
+
+ include($phpbb_root_path . 'includes/functions_posting.' . $phpEx);
+ include($phpbb_root_path . 'includes/functions_display.' . $phpEx);
+ include($phpbb_root_path . 'includes/message_parser.' . $phpEx);
+
+ if (!$action)
+ {
+ $action = 'post';
+ }
+ add_form_key('ucp_pm_compose');
+
+ // Grab only parameters needed here
+ $to_user_id = request_var('u', 0);
+ $to_group_id = request_var('g', 0);
+ $msg_id = request_var('p', 0);
+ $draft_id = request_var('d', 0);
+ $lastclick = request_var('lastclick', 0);
+
+ // Reply to all triggered (quote/reply)
+ $reply_to_all = request_var('reply_to_all', 0);
+
+ // Do NOT use request_var or specialchars here
+ $address_list = isset($_REQUEST['address_list']) ? $_REQUEST['address_list'] : array();
+
+ if (!is_array($address_list))
+ {
+ $address_list = array();
+ }
+
+ $submit = (isset($_POST['post'])) ? true : false;
+ $preview = (isset($_POST['preview'])) ? true : false;
+ $save = (isset($_POST['save'])) ? true : false;
+ $load = (isset($_POST['load'])) ? true : false;
+ $cancel = (isset($_POST['cancel']) && !isset($_POST['save'])) ? true : false;
+ $delete = (isset($_POST['delete'])) ? true : false;
+
+ $remove_u = (isset($_REQUEST['remove_u'])) ? true : false;
+ $remove_g = (isset($_REQUEST['remove_g'])) ? true : false;
+ $add_to = (isset($_REQUEST['add_to'])) ? true : false;
+ $add_bcc = (isset($_REQUEST['add_bcc'])) ? true : false;
+
+ $refresh = isset($_POST['add_file']) || isset($_POST['delete_file']) || $save || $load
+ || $remove_u || $remove_g || $add_to || $add_bcc;
+
+ $action = ($delete && !$preview && !$refresh && $submit) ? 'delete' : $action;
+ $select_single = ($config['allow_mass_pm'] && $auth->acl_get('u_masspm')) ? false : true;
+
+ $error = array();
+ $current_time = time();
+
+ // Was cancel pressed? If so then redirect to the appropriate page
+ if ($cancel || ($current_time - $lastclick < 2 && $submit))
+ {
+ if ($msg_id)
+ {
+ redirect(append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;mode=view&amp;action=view_message&amp;p=' . $msg_id));
+ }
+ redirect(append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm'));
+ }
+
+ // Since viewtopic.php language entries are used in several modes,
+ // we include the language file here
+ $user->add_lang('viewtopic');
+
+ // Output PM_TO box if message composing
+ if ($action != 'edit')
+ {
+ // Add groups to PM box
+ if ($config['allow_mass_pm'] && $auth->acl_get('u_masspm_group'))
+ {
+ $sql = 'SELECT g.group_id, g.group_name, g.group_type
+ FROM ' . GROUPS_TABLE . ' g';
+
+ if (!$auth->acl_gets('a_group', 'a_groupadd', 'a_groupdel'))
+ {
+ $sql .= ' LEFT JOIN ' . USER_GROUP_TABLE . ' ug
+ ON (
+ g.group_id = ug.group_id
+ AND ug.user_id = ' . $user->data['user_id'] . '
+ AND ug.user_pending = 0
+ )
+ WHERE (g.group_type <> ' . GROUP_HIDDEN . ' OR ug.user_id = ' . $user->data['user_id'] . ')';
+ }
+
+ $sql .= ($auth->acl_gets('a_group', 'a_groupadd', 'a_groupdel')) ? ' WHERE ' : ' AND ';
+
+ $sql .= 'g.group_receive_pm = 1
+ ORDER BY g.group_type DESC, g.group_name ASC';
+ $result = $db->sql_query($sql);
+
+ $group_options = '';
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $group_options .= '<option' . (($row['group_type'] == GROUP_SPECIAL) ? ' class="sep"' : '') . ' value="' . $row['group_id'] . '">' . (($row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $row['group_name']] : $row['group_name']) . '</option>';
+ }
+ $db->sql_freeresult($result);
+ }
+
+ $template->assign_vars(array(
+ 'S_SHOW_PM_BOX' => true,
+ 'S_ALLOW_MASS_PM' => ($config['allow_mass_pm'] && $auth->acl_get('u_masspm')) ? true : false,
+ 'S_GROUP_OPTIONS' => ($config['allow_mass_pm'] && $auth->acl_get('u_masspm_group')) ? $group_options : '',
+ 'U_FIND_USERNAME' => append_sid("{$phpbb_root_path}memberlist.$phpEx", "mode=searchuser&amp;form=postform&amp;field=username_list&amp;select_single=$select_single"),
+ ));
+ }
+
+ $sql = '';
+
+ // What is all this following SQL for? Well, we need to know
+ // some basic information in all cases before we do anything.
+ switch ($action)
+ {
+ case 'post':
+ if (!$auth->acl_get('u_sendpm'))
+ {
+ trigger_error('NO_AUTH_SEND_MESSAGE');
+ }
+ break;
+
+ case 'reply':
+ case 'quote':
+ case 'forward':
+ case 'quotepost':
+ if (!$msg_id)
+ {
+ trigger_error('NO_MESSAGE');
+ }
+
+ if (!$auth->acl_get('u_sendpm'))
+ {
+ trigger_error('NO_AUTH_SEND_MESSAGE');
+ }
+
+ if ($action == 'quotepost')
+ {
+ $sql = 'SELECT p.post_id as msg_id, p.forum_id, p.post_text as message_text, p.poster_id as author_id, p.post_time as message_time, p.bbcode_bitfield, p.bbcode_uid, p.enable_sig, p.enable_smilies, p.enable_magic_url, t.topic_title as message_subject, u.username as quote_username
+ FROM ' . POSTS_TABLE . ' p, ' . TOPICS_TABLE . ' t, ' . USERS_TABLE . " u
+ WHERE p.post_id = $msg_id
+ AND t.topic_id = p.topic_id
+ AND u.user_id = p.poster_id";
+ }
+ else
+ {
+ $sql = 'SELECT t.folder_id, p.*, u.username as quote_username
+ FROM ' . PRIVMSGS_TO_TABLE . ' t, ' . PRIVMSGS_TABLE . ' p, ' . USERS_TABLE . ' u
+ WHERE t.user_id = ' . $user->data['user_id'] . "
+ AND p.author_id = u.user_id
+ AND t.msg_id = p.msg_id
+ AND p.msg_id = $msg_id";
+ }
+ break;
+
+ case 'edit':
+ if (!$msg_id)
+ {
+ trigger_error('NO_MESSAGE');
+ }
+
+ // check for outbox (not read) status, we do not allow editing if one user already having the message
+ $sql = 'SELECT p.*, t.folder_id
+ FROM ' . PRIVMSGS_TO_TABLE . ' t, ' . PRIVMSGS_TABLE . ' p
+ WHERE t.user_id = ' . $user->data['user_id'] . '
+ AND t.folder_id = ' . PRIVMSGS_OUTBOX . "
+ AND t.msg_id = $msg_id
+ AND t.msg_id = p.msg_id";
+ break;
+
+ case 'delete':
+ if (!$auth->acl_get('u_pm_delete'))
+ {
+ trigger_error('NO_AUTH_DELETE_MESSAGE');
+ }
+
+ if (!$msg_id)
+ {
+ trigger_error('NO_MESSAGE');
+ }
+
+ $sql = 'SELECT msg_id, pm_unread, pm_new, author_id, folder_id
+ FROM ' . PRIVMSGS_TO_TABLE . '
+ WHERE user_id = ' . $user->data['user_id'] . "
+ AND msg_id = $msg_id";
+ break;
+
+ case 'smilies':
+ generate_smilies('window', 0);
+ break;
+
+ default:
+ trigger_error('NO_ACTION_MODE', E_USER_ERROR);
+ break;
+ }
+
+ if ($action == 'forward' && (!$config['forward_pm'] || !$auth->acl_get('u_pm_forward')))
+ {
+ trigger_error('NO_AUTH_FORWARD_MESSAGE');
+ }
+
+ if ($action == 'edit' && !$auth->acl_get('u_pm_edit'))
+ {
+ trigger_error('NO_AUTH_EDIT_MESSAGE');
+ }
+
+ if ($sql)
+ {
+ $result = $db->sql_query($sql);
+ $post = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if (!$post)
+ {
+ // If editing it could be the recipient already read the message...
+ if ($action == 'edit')
+ {
+ $sql = 'SELECT p.*, t.folder_id
+ FROM ' . PRIVMSGS_TO_TABLE . ' t, ' . PRIVMSGS_TABLE . ' p
+ WHERE t.user_id = ' . $user->data['user_id'] . "
+ AND t.msg_id = $msg_id
+ AND t.msg_id = p.msg_id";
+ $result = $db->sql_query($sql);
+ $post = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if ($post)
+ {
+ trigger_error('NO_EDIT_READ_MESSAGE');
+ }
+ }
+
+ trigger_error('NO_MESSAGE');
+ }
+
+ if ($action == 'quotepost')
+ {
+ if (($post['forum_id'] && !$auth->acl_get('f_read', $post['forum_id'])) || (!$post['forum_id'] && !$auth->acl_getf_global('f_read')))
+ {
+ trigger_error('NOT_AUTHORISED');
+ }
+
+ // Passworded forum?
+ if ($post['forum_id'])
+ {
+ $sql = 'SELECT forum_password
+ FROM ' . FORUMS_TABLE . '
+ WHERE forum_id = ' . (int) $post['forum_id'];
+ $result = $db->sql_query($sql);
+ $forum_password = (string) $db->sql_fetchfield('forum_password');
+ $db->sql_freeresult($result);
+
+ if ($forum_password)
+ {
+ login_forum_box(array(
+ 'forum_id' => $post['forum_id'],
+ 'forum_password' => $forum_password,
+ ));
+ }
+ }
+ }
+
+ $msg_id = (int) $post['msg_id'];
+ $folder_id = (isset($post['folder_id'])) ? $post['folder_id'] : 0;
+ $message_text = (isset($post['message_text'])) ? $post['message_text'] : '';
+
+ if ((!$post['author_id'] || ($post['author_id'] == ANONYMOUS && $action != 'delete')) && $msg_id)
+ {
+ trigger_error('NO_AUTHOR');
+ }
+
+ if ($action == 'quotepost')
+ {
+ // Decode text for message display
+ decode_message($message_text, $post['bbcode_uid']);
+ }
+
+ if ($action != 'delete')
+ {
+ $enable_urls = $post['enable_magic_url'];
+ $enable_sig = (isset($post['enable_sig'])) ? $post['enable_sig'] : 0;
+
+ $message_attachment = (isset($post['message_attachment'])) ? $post['message_attachment'] : 0;
+ $message_subject = $post['message_subject'];
+ $message_time = $post['message_time'];
+ $bbcode_uid = $post['bbcode_uid'];
+
+ $quote_username = (isset($post['quote_username'])) ? $post['quote_username'] : '';
+ $icon_id = (isset($post['icon_id'])) ? $post['icon_id'] : 0;
+
+ if (($action == 'reply' || $action == 'quote' || $action == 'quotepost') && !sizeof($address_list) && !$refresh && !$submit && !$preview)
+ {
+ // Add the original author as the recipient if quoting a post or only replying and not having checked "reply to all"
+ if ($action == 'quotepost' || !$reply_to_all)
+ {
+ $address_list = array('u' => array($post['author_id'] => 'to'));
+ }
+ else
+ {
+ // We try to include every previously listed member from the TO Header - Reply to all
+ $address_list = rebuild_header(array('to' => $post['to_address']));
+
+ // Add the author (if he is already listed then this is no shame (it will be overwritten))
+ $address_list['u'][$post['author_id']] = 'to';
+
+ // Now, make sure the user itself is not listed. ;)
+ if (isset($address_list['u'][$user->data['user_id']]))
+ {
+ unset($address_list['u'][$user->data['user_id']]);
+ }
+ }
+ }
+ else if ($action == 'edit' && !sizeof($address_list) && !$refresh && !$submit && !$preview)
+ {
+ // Rebuild TO and BCC Header
+ $address_list = rebuild_header(array('to' => $post['to_address'], 'bcc' => $post['bcc_address']));
+ }
+
+ if ($action == 'quotepost')
+ {
+ $check_value = 0;
+ }
+ else
+ {
+ $check_value = (($post['enable_bbcode']+1) << 8) + (($post['enable_smilies']+1) << 4) + (($enable_urls+1) << 2) + (($post['enable_sig']+1) << 1);
+ }
+ }
+ }
+ else
+ {
+ $message_attachment = 0;
+ $message_text = $message_subject = '';
+
+ if ($to_user_id && $action == 'post')
+ {
+ $address_list['u'][$to_user_id] = 'to';
+ }
+ else if ($to_group_id && $action == 'post')
+ {
+ $address_list['g'][$to_group_id] = 'to';
+ }
+ $check_value = 0;
+ }
+
+ if (($to_group_id || isset($address_list['g'])) && (!$config['allow_mass_pm'] || !$auth->acl_get('u_masspm_group')))
+ {
+ trigger_error('NO_AUTH_GROUP_MESSAGE');
+ }
+
+ if ($action == 'edit' && !$refresh && !$preview && !$submit)
+ {
+ if (!($message_time > time() - ($config['pm_edit_time'] * 60) || !$config['pm_edit_time']))
+ {
+ trigger_error('CANNOT_EDIT_MESSAGE_TIME');
+ }
+ }
+
+ if ($action == 'post')
+ {
+ $template->assign_var('S_NEW_MESSAGE', true);
+ }
+
+ if (!isset($icon_id))
+ {
+ $icon_id = 0;
+ }
+
+ $message_parser = new parse_message();
+
+ $message_parser->message = ($action == 'reply') ? '' : $message_text;
+ unset($message_text);
+
+ $s_action = append_sid("{$phpbb_root_path}ucp.$phpEx", "i=$id&amp;mode=$mode&amp;action=$action", true, $user->session_id);
+ $s_action .= ($msg_id) ? "&amp;p=$msg_id" : '';
+
+ // Delete triggered ?
+ if ($action == 'delete')
+ {
+ // Folder id has been determined by the SQL Statement
+ // $folder_id = request_var('f', PRIVMSGS_NO_BOX);
+
+ // Do we need to confirm ?
+ if (confirm_box(true))
+ {
+ delete_pm($user->data['user_id'], $msg_id, $folder_id);
+
+ // jump to next message in "history"? nope, not for the moment. But able to be included later.
+ $meta_info = append_sid("{$phpbb_root_path}ucp.$phpEx", "i=pm&amp;folder=$folder_id");
+ $message = $user->lang['MESSAGE_DELETED'];
+
+ meta_refresh(3, $meta_info);
+ $message .= '<br /><br />' . sprintf($user->lang['RETURN_FOLDER'], '<a href="' . $meta_info . '">', '</a>');
+ trigger_error($message);
+ }
+ else
+ {
+ $s_hidden_fields = array(
+ 'p' => $msg_id,
+ 'f' => $folder_id,
+ 'action' => 'delete'
+ );
+
+ // "{$phpbb_root_path}ucp.$phpEx?i=pm&amp;mode=compose"
+ confirm_box(false, 'DELETE_MESSAGE', build_hidden_fields($s_hidden_fields));
+ }
+
+ redirect(append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;mode=view&amp;action=view_message&amp;p=' . $msg_id));
+ }
+
+ // Get maximum number of allowed recipients
+ $sql = 'SELECT MAX(g.group_max_recipients) as max_recipients
+ FROM ' . GROUPS_TABLE . ' g, ' . USER_GROUP_TABLE . ' ug
+ WHERE ug.user_id = ' . $user->data['user_id'] . '
+ AND ug.user_pending = 0
+ AND ug.group_id = g.group_id';
+ $result = $db->sql_query($sql);
+ $max_recipients = (int) $db->sql_fetchfield('max_recipients');
+ $db->sql_freeresult($result);
+
+ $max_recipients = (!$max_recipients) ? $config['pm_max_recipients'] : $max_recipients;
+
+ // If this is a quote/reply "to all"... we may increase the max_recpients to the number of original recipients
+ if (($action == 'reply' || $action == 'quote') && $max_recipients && $reply_to_all)
+ {
+ // We try to include every previously listed member from the TO Header
+ $list = rebuild_header(array('to' => $post['to_address']));
+
+ // Can be an empty array too ;)
+ $list = (!empty($list['u'])) ? $list['u'] : array();
+ $list[$post['author_id']] = 'to';
+
+ if (isset($list[$user->data['user_id']]))
+ {
+ unset($list[$user->data['user_id']]);
+ }
+
+ $max_recipients = ($max_recipients < sizeof($list)) ? sizeof($list) : $max_recipients;
+
+ unset($list);
+ }
+
+ // Handle User/Group adding/removing
+ handle_message_list_actions($address_list, $error, $remove_u, $remove_g, $add_to, $add_bcc);
+
+ // Check mass pm to group permission
+ if ((!$config['allow_mass_pm'] || !$auth->acl_get('u_masspm_group')) && !empty($address_list['g']))
+ {
+ $address_list = array();
+ $error[] = $user->lang['NO_AUTH_GROUP_MESSAGE'];
+ }
+
+ // Check mass pm to users permission
+ if ((!$config['allow_mass_pm'] || !$auth->acl_get('u_masspm')) && num_recipients($address_list) > 1)
+ {
+ $address_list = get_recipients($address_list, 1);
+ $error[] = $user->lang('TOO_MANY_RECIPIENTS', 1);
+ }
+
+ // Check for too many recipients
+ if (!empty($address_list['u']) && $max_recipients && sizeof($address_list['u']) > $max_recipients)
+ {
+ $address_list = get_recipients($address_list, $max_recipients);
+ $error[] = $user->lang('TOO_MANY_RECIPIENTS', $max_recipients);
+ }
+
+ // Always check if the submitted attachment data is valid and belongs to the user.
+ // Further down (especially in submit_post()) we do not check this again.
+ $message_parser->get_submitted_attachment_data();
+
+ if ($message_attachment && !$submit && !$refresh && !$preview && $action == 'edit')
+ {
+ // Do not change to SELECT *
+ $sql = 'SELECT attach_id, is_orphan, attach_comment, real_filename
+ FROM ' . ATTACHMENTS_TABLE . "
+ WHERE post_msg_id = $msg_id
+ AND in_message = 1
+ AND is_orphan = 0
+ ORDER BY filetime DESC";
+ $result = $db->sql_query($sql);
+ $message_parser->attachment_data = array_merge($message_parser->attachment_data, $db->sql_fetchrowset($result));
+ $db->sql_freeresult($result);
+ }
+
+ if (!in_array($action, array('quote', 'edit', 'delete', 'forward')))
+ {
+ $enable_sig = ($config['allow_sig'] && $config['allow_sig_pm'] && $auth->acl_get('u_sig') && $user->optionget('attachsig'));
+ $enable_smilies = ($config['allow_smilies'] && $auth->acl_get('u_pm_smilies') && $user->optionget('smilies'));
+ $enable_bbcode = ($config['allow_bbcode'] && $auth->acl_get('u_pm_bbcode') && $user->optionget('bbcode'));
+ $enable_urls = true;
+ }
+
+ $enable_magic_url = $drafts = false;
+
+ // User own some drafts?
+ if ($auth->acl_get('u_savedrafts') && $action != 'delete')
+ {
+ $sql = 'SELECT draft_id
+ FROM ' . DRAFTS_TABLE . '
+ WHERE forum_id = 0
+ AND topic_id = 0
+ AND user_id = ' . $user->data['user_id'] .
+ (($draft_id) ? " AND draft_id <> $draft_id" : '');
+ $result = $db->sql_query_limit($sql, 1);
+ $row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if ($row)
+ {
+ $drafts = true;
+ }
+ }
+
+ if ($action == 'edit')
+ {
+ $message_parser->bbcode_uid = $bbcode_uid;
+ }
+
+ $bbcode_status = ($config['allow_bbcode'] && $config['auth_bbcode_pm'] && $auth->acl_get('u_pm_bbcode')) ? true : false;
+ $smilies_status = ($config['allow_smilies'] && $config['auth_smilies_pm'] && $auth->acl_get('u_pm_smilies')) ? true : false;
+ $img_status = ($config['auth_img_pm'] && $auth->acl_get('u_pm_img')) ? true : false;
+ $flash_status = ($config['auth_flash_pm'] && $auth->acl_get('u_pm_flash')) ? true : false;
+ $url_status = ($config['allow_post_links']) ? true : false;
+
+ // Save Draft
+ if ($save && $auth->acl_get('u_savedrafts'))
+ {
+ $subject = utf8_normalize_nfc(request_var('subject', '', true));
+ $subject = (!$subject && $action != 'post') ? $user->lang['NEW_MESSAGE'] : $subject;
+ $message = utf8_normalize_nfc(request_var('message', '', true));
+
+ if ($subject && $message)
+ {
+ if (confirm_box(true))
+ {
+ $sql = 'INSERT INTO ' . DRAFTS_TABLE . ' ' . $db->sql_build_array('INSERT', array(
+ 'user_id' => $user->data['user_id'],
+ 'topic_id' => 0,
+ 'forum_id' => 0,
+ 'save_time' => $current_time,
+ 'draft_subject' => $subject,
+ 'draft_message' => $message
+ )
+ );
+ $db->sql_query($sql);
+
+ $redirect_url = append_sid("{$phpbb_root_path}ucp.$phpEx", "i=pm&amp;mode=$mode");
+
+ meta_refresh(3, $redirect_url);
+ $message = $user->lang['DRAFT_SAVED'] . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $redirect_url . '">', '</a>');
+
+ trigger_error($message);
+ }
+ else
+ {
+ $s_hidden_fields = build_hidden_fields(array(
+ 'mode' => $mode,
+ 'action' => $action,
+ 'save' => true,
+ 'subject' => $subject,
+ 'message' => $message,
+ 'u' => $to_user_id,
+ 'g' => $to_group_id,
+ 'p' => $msg_id)
+ );
+ $s_hidden_fields .= build_address_field($address_list);
+
+
+ confirm_box(false, 'SAVE_DRAFT', $s_hidden_fields);
+ }
+ }
+ else
+ {
+ if (utf8_clean_string($subject) === '')
+ {
+ $error[] = $user->lang['EMPTY_MESSAGE_SUBJECT'];
+ }
+
+ if (utf8_clean_string($message) === '')
+ {
+ $error[] = $user->lang['TOO_FEW_CHARS'];
+ }
+ }
+
+ unset($subject, $message);
+ }
+
+ // Load Draft
+ if ($draft_id && $auth->acl_get('u_savedrafts'))
+ {
+ $sql = 'SELECT draft_subject, draft_message
+ FROM ' . DRAFTS_TABLE . "
+ WHERE draft_id = $draft_id
+ AND topic_id = 0
+ AND forum_id = 0
+ AND user_id = " . $user->data['user_id'];
+ $result = $db->sql_query_limit($sql, 1);
+
+ if ($row = $db->sql_fetchrow($result))
+ {
+ $message_parser->message = $row['draft_message'];
+ $message_subject = $row['draft_subject'];
+
+ $template->assign_var('S_DRAFT_LOADED', true);
+ }
+ else
+ {
+ $draft_id = 0;
+ }
+ $db->sql_freeresult($result);
+ }
+
+ // Load Drafts
+ if ($load && $drafts)
+ {
+ load_drafts(0, 0, $id, $action, $msg_id);
+ }
+
+ if ($submit || $preview || $refresh)
+ {
+ if (($submit || $preview) && !check_form_key('ucp_pm_compose'))
+ {
+ $error[] = $user->lang['FORM_INVALID'];
+ }
+ $subject = utf8_normalize_nfc(request_var('subject', '', true));
+ $message_parser->message = utf8_normalize_nfc(request_var('message', '', true));
+
+ $icon_id = request_var('icon', 0);
+
+ $enable_bbcode = (!$bbcode_status || isset($_POST['disable_bbcode'])) ? false : true;
+ $enable_smilies = (!$smilies_status || isset($_POST['disable_smilies'])) ? false : true;
+ $enable_urls = (isset($_POST['disable_magic_url'])) ? 0 : 1;
+ $enable_sig = (!$config['allow_sig'] ||!$config['allow_sig_pm']) ? false : ((isset($_POST['attach_sig'])) ? true : false);
+
+ if ($submit)
+ {
+ $status_switch = (($enable_bbcode+1) << 8) + (($enable_smilies+1) << 4) + (($enable_urls+1) << 2) + (($enable_sig+1) << 1);
+ $status_switch = ($status_switch != $check_value);
+ }
+ else
+ {
+ $status_switch = 1;
+ }
+
+ // Parse Attachments - before checksum is calculated
+ $message_parser->parse_attachments('fileupload', $action, 0, $submit, $preview, $refresh, true);
+
+ if (sizeof($message_parser->warn_msg) && !($remove_u || $remove_g || $add_to || $add_bcc))
+ {
+ $error[] = implode('<br />', $message_parser->warn_msg);
+ $message_parser->warn_msg = array();
+ }
+
+ // Parse message
+ $message_parser->parse($enable_bbcode, ($config['allow_post_links']) ? $enable_urls : false, $enable_smilies, $img_status, $flash_status, true, $config['allow_post_links']);
+
+ // On a refresh we do not care about message parsing errors
+ if (sizeof($message_parser->warn_msg) && !$refresh)
+ {
+ $error[] = implode('<br />', $message_parser->warn_msg);
+ }
+
+ if ($action != 'edit' && !$preview && !$refresh && $config['flood_interval'] && !$auth->acl_get('u_ignoreflood'))
+ {
+ // Flood check
+ $last_post_time = $user->data['user_lastpost_time'];
+
+ if ($last_post_time)
+ {
+ if ($last_post_time && ($current_time - $last_post_time) < intval($config['flood_interval']))
+ {
+ $error[] = $user->lang['FLOOD_ERROR'];
+ }
+ }
+ }
+
+ // Subject defined
+ if ($submit)
+ {
+ if (utf8_clean_string($subject) === '')
+ {
+ $error[] = $user->lang['EMPTY_MESSAGE_SUBJECT'];
+ }
+
+ if (!sizeof($address_list))
+ {
+ $error[] = $user->lang['NO_RECIPIENT'];
+ }
+ }
+
+ // Store message, sync counters
+ if (!sizeof($error) && $submit)
+ {
+ $pm_data = array(
+ 'msg_id' => (int) $msg_id,
+ 'from_user_id' => $user->data['user_id'],
+ 'from_user_ip' => $user->ip,
+ 'from_username' => $user->data['username'],
+ 'reply_from_root_level' => (isset($post['root_level'])) ? (int) $post['root_level'] : 0,
+ 'reply_from_msg_id' => (int) $msg_id,
+ 'icon_id' => (int) $icon_id,
+ 'enable_sig' => (bool) $enable_sig,
+ 'enable_bbcode' => (bool) $enable_bbcode,
+ 'enable_smilies' => (bool) $enable_smilies,
+ 'enable_urls' => (bool) $enable_urls,
+ 'bbcode_bitfield' => $message_parser->bbcode_bitfield,
+ 'bbcode_uid' => $message_parser->bbcode_uid,
+ 'message' => $message_parser->message,
+ 'attachment_data' => $message_parser->attachment_data,
+ 'filename_data' => $message_parser->filename_data,
+ 'address_list' => $address_list
+ );
+
+ // ((!$message_subject) ? $subject : $message_subject)
+ $msg_id = submit_pm($action, $subject, $pm_data);
+
+ $return_message_url = append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;mode=view&amp;p=' . $msg_id);
+ $return_folder_url = append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;folder=outbox');
+ meta_refresh(3, $return_message_url);
+
+ $message = $user->lang['MESSAGE_STORED'] . '<br /><br />' . sprintf($user->lang['VIEW_PRIVATE_MESSAGE'], '<a href="' . $return_message_url . '">', '</a>') . '<br /><br />' . sprintf($user->lang['CLICK_RETURN_FOLDER'], '<a href="' . $return_folder_url . '">', '</a>', $user->lang['PM_OUTBOX']);
+ trigger_error($message);
+ }
+
+ $message_subject = $subject;
+ }
+
+ // Preview
+ if (!sizeof($error) && $preview)
+ {
+ $preview_message = $message_parser->format_display($enable_bbcode, $enable_urls, $enable_smilies, false);
+
+ $preview_signature = $user->data['user_sig'];
+ $preview_signature_uid = $user->data['user_sig_bbcode_uid'];
+ $preview_signature_bitfield = $user->data['user_sig_bbcode_bitfield'];
+
+ // Signature
+ if ($enable_sig && $config['allow_sig'] && $preview_signature)
+ {
+ $parse_sig = new parse_message($preview_signature);
+ $parse_sig->bbcode_uid = $preview_signature_uid;
+ $parse_sig->bbcode_bitfield = $preview_signature_bitfield;
+
+ $parse_sig->format_display($enable_bbcode, $enable_urls, $enable_smilies);
+ $preview_signature = $parse_sig->message;
+ unset($parse_sig);
+ }
+ else
+ {
+ $preview_signature = '';
+ }
+
+ // Attachment Preview
+ if (sizeof($message_parser->attachment_data))
+ {
+ $template->assign_var('S_HAS_ATTACHMENTS', true);
+
+ $update_count = array();
+ $attachment_data = $message_parser->attachment_data;
+
+ parse_attachments(false, $preview_message, $attachment_data, $update_count, true);
+
+ foreach ($attachment_data as $i => $attachment)
+ {
+ $template->assign_block_vars('attachment', array(
+ 'DISPLAY_ATTACHMENT' => $attachment)
+ );
+ }
+ unset($attachment_data);
+ }
+
+ $preview_subject = censor_text($subject);
+
+ if (!sizeof($error))
+ {
+ $template->assign_vars(array(
+ 'PREVIEW_SUBJECT' => $preview_subject,
+ 'PREVIEW_MESSAGE' => $preview_message,
+ 'PREVIEW_SIGNATURE' => $preview_signature,
+
+ 'S_DISPLAY_PREVIEW' => true)
+ );
+ }
+ unset($message_text);
+ }
+
+ // Decode text for message display
+ $bbcode_uid = (($action == 'quote' || $action == 'forward') && !$preview && !$refresh) ? $bbcode_uid : $message_parser->bbcode_uid;
+
+ $message_parser->decode_message($bbcode_uid);
+
+ if (($action == 'quote' || $action == 'quotepost') && !$preview && !$refresh && !$submit)
+ {
+ if ($action == 'quotepost')
+ {
+ $post_id = request_var('p', 0);
+ if ($config['allow_post_links'])
+ {
+ $message_link = "[url=" . generate_board_url() . "/viewtopic.$phpEx?p={$post_id}#p{$post_id}]{$user->lang['SUBJECT']}: {$message_subject}[/url]\n\n";
+ }
+ else
+ {
+ $message_link = $user->lang['SUBJECT'] . ': ' . $message_subject . " (" . generate_board_url() . "/viewtopic.$phpEx?p={$post_id}#p{$post_id})\n\n";
+ }
+ }
+ else
+ {
+ $message_link = '';
+ }
+ $message_parser->message = $message_link . '[quote=&quot;' . $quote_username . '&quot;]' . censor_text(trim($message_parser->message)) . "[/quote]\n";
+ }
+
+ if (($action == 'reply' || $action == 'quote' || $action == 'quotepost') && !$preview && !$refresh)
+ {
+ $message_subject = ((!preg_match('/^Re:/', $message_subject)) ? 'Re: ' : '') . censor_text($message_subject);
+ }
+
+ if ($action == 'forward' && !$preview && !$refresh && !$submit)
+ {
+ $fwd_to_field = write_pm_addresses(array('to' => $post['to_address']), 0, true);
+
+ if ($config['allow_post_links'])
+ {
+ $quote_username_text = '[url=' . generate_board_url() . "/memberlist.$phpEx?mode=viewprofile&amp;u={$post['author_id']}]{$quote_username}[/url]";
+ }
+ else
+ {
+ $quote_username_text = $quote_username . ' (' . generate_board_url() . "/memberlist.$phpEx?mode=viewprofile&amp;u={$post['author_id']})";
+ }
+
+ $forward_text = array();
+ $forward_text[] = $user->lang['FWD_ORIGINAL_MESSAGE'];
+ $forward_text[] = sprintf($user->lang['FWD_SUBJECT'], censor_text($message_subject));
+ $forward_text[] = sprintf($user->lang['FWD_DATE'], $user->format_date($message_time, false, true));
+ $forward_text[] = sprintf($user->lang['FWD_FROM'], $quote_username_text);
+ $forward_text[] = sprintf($user->lang['FWD_TO'], implode(', ', $fwd_to_field['to']));
+
+ $message_parser->message = implode("\n", $forward_text) . "\n\n[quote=&quot;{$quote_username}&quot;]\n" . censor_text(trim($message_parser->message)) . "\n[/quote]";
+ $message_subject = ((!preg_match('/^Fwd:/', $message_subject)) ? 'Fwd: ' : '') . censor_text($message_subject);
+ }
+
+ $attachment_data = $message_parser->attachment_data;
+ $filename_data = $message_parser->filename_data;
+ $message_text = $message_parser->message;
+
+ // MAIN PM PAGE BEGINS HERE
+
+ // Generate smiley listing
+ generate_smilies('inline', 0);
+
+ // Generate PM Icons
+ $s_pm_icons = false;
+ if ($config['enable_pm_icons'])
+ {
+ $s_pm_icons = posting_gen_topic_icons($action, $icon_id);
+ }
+
+ // Generate inline attachment select box
+ posting_gen_inline_attachments($attachment_data);
+
+ // Build address list for display
+ // array('u' => array($author_id => 'to'));
+ if (sizeof($address_list))
+ {
+ // Get Usernames and Group Names
+ $result = array();
+ if (!empty($address_list['u']))
+ {
+ $sql = 'SELECT user_id as id, username as name, user_colour as colour
+ FROM ' . USERS_TABLE . '
+ WHERE ' . $db->sql_in_set('user_id', array_map('intval', array_keys($address_list['u']))) . '
+ ORDER BY username_clean ASC';
+ $result['u'] = $db->sql_query($sql);
+ }
+
+ if (!empty($address_list['g']))
+ {
+ $sql = 'SELECT g.group_id AS id, g.group_name AS name, g.group_colour AS colour, g.group_type
+ FROM ' . GROUPS_TABLE . ' g';
+
+ if (!$auth->acl_gets('a_group', 'a_groupadd', 'a_groupdel'))
+ {
+ $sql .= ' LEFT JOIN ' . USER_GROUP_TABLE . ' ug
+ ON (
+ g.group_id = ug.group_id
+ AND ug.user_id = ' . $user->data['user_id'] . '
+ AND ug.user_pending = 0
+ )
+ WHERE (g.group_type <> ' . GROUP_HIDDEN . ' OR ug.user_id = ' . $user->data['user_id'] . ')';
+ }
+
+ $sql .= ($auth->acl_gets('a_group', 'a_groupadd', 'a_groupdel')) ? ' WHERE ' : ' AND ';
+
+ $sql .= 'g.group_receive_pm = 1
+ AND ' . $db->sql_in_set('g.group_id', array_map('intval', array_keys($address_list['g']))) . '
+ ORDER BY g.group_name ASC';
+
+ $result['g'] = $db->sql_query($sql);
+ }
+
+ $u = $g = array();
+ $_types = array('u', 'g');
+ foreach ($_types as $type)
+ {
+ if (isset($result[$type]) && $result[$type])
+ {
+ while ($row = $db->sql_fetchrow($result[$type]))
+ {
+ if ($type == 'g')
+ {
+ $row['name'] = ($row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $row['name']] : $row['name'];
+ }
+
+ ${$type}[$row['id']] = array('name' => $row['name'], 'colour' => $row['colour']);
+ }
+ $db->sql_freeresult($result[$type]);
+ }
+ }
+
+ // Now Build the address list
+ $plain_address_field = '';
+ foreach ($address_list as $type => $adr_ary)
+ {
+ foreach ($adr_ary as $id => $field)
+ {
+ if (!isset(${$type}[$id]))
+ {
+ unset($address_list[$type][$id]);
+ continue;
+ }
+
+ $field = ($field == 'to') ? 'to' : 'bcc';
+ $type = ($type == 'u') ? 'u' : 'g';
+ $id = (int) $id;
+
+ $tpl_ary = array(
+ 'IS_GROUP' => ($type == 'g') ? true : false,
+ 'IS_USER' => ($type == 'u') ? true : false,
+ 'UG_ID' => $id,
+ 'NAME' => ${$type}[$id]['name'],
+ 'COLOUR' => (${$type}[$id]['colour']) ? '#' . ${$type}[$id]['colour'] : '',
+ 'TYPE' => $type,
+ );
+
+ if ($type == 'u')
+ {
+ $tpl_ary = array_merge($tpl_ary, array(
+ 'U_VIEW' => get_username_string('profile', $id, ${$type}[$id]['name'], ${$type}[$id]['colour']),
+ 'NAME_FULL' => get_username_string('full', $id, ${$type}[$id]['name'], ${$type}[$id]['colour']),
+ ));
+ }
+ else
+ {
+ $tpl_ary = array_merge($tpl_ary, array(
+ 'U_VIEW' => append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=group&amp;g=' . $id),
+ ));
+ }
+
+ $template->assign_block_vars($field . '_recipient', $tpl_ary);
+ }
+ }
+ }
+
+ // Build hidden address list
+ $s_hidden_address_field = build_address_field($address_list);
+
+
+ $bbcode_checked = (isset($enable_bbcode)) ? !$enable_bbcode : (($config['allow_bbcode'] && $auth->acl_get('u_pm_bbcode')) ? !$user->optionget('bbcode') : 1);
+ $smilies_checked = (isset($enable_smilies)) ? !$enable_smilies : (($config['allow_smilies'] && $auth->acl_get('u_pm_smilies')) ? !$user->optionget('smilies') : 1);
+ $urls_checked = (isset($enable_urls)) ? !$enable_urls : 0;
+ $sig_checked = $enable_sig;
+
+ switch ($action)
+ {
+ case 'post':
+ $page_title = $user->lang['POST_NEW_PM'];
+ break;
+
+ case 'quote':
+ $page_title = $user->lang['POST_QUOTE_PM'];
+ break;
+
+ case 'quotepost':
+ $page_title = $user->lang['POST_PM_POST'];
+ break;
+
+ case 'reply':
+ $page_title = $user->lang['POST_REPLY_PM'];
+ break;
+
+ case 'edit':
+ $page_title = $user->lang['POST_EDIT_PM'];
+ break;
+
+ case 'forward':
+ $page_title = $user->lang['POST_FORWARD_PM'];
+ break;
+
+ default:
+ trigger_error('NO_ACTION_MODE', E_USER_ERROR);
+ break;
+ }
+
+ $s_hidden_fields = '<input type="hidden" name="lastclick" value="' . $current_time . '" />';
+ $s_hidden_fields .= (isset($check_value)) ? '<input type="hidden" name="status_switch" value="' . $check_value . '" />' : '';
+ $s_hidden_fields .= ($draft_id || isset($_REQUEST['draft_loaded'])) ? '<input type="hidden" name="draft_loaded" value="' . ((isset($_REQUEST['draft_loaded'])) ? intval($_REQUEST['draft_loaded']) : $draft_id) . '" />' : '';
+
+ $form_enctype = (@ini_get('file_uploads') == '0' || strtolower(@ini_get('file_uploads')) == 'off' || !$config['allow_pm_attach'] || !$auth->acl_get('u_pm_attach')) ? '' : ' enctype="multipart/form-data"';
+
+ // Start assigning vars for main posting page ...
+ $template->assign_vars(array(
+ 'L_POST_A' => $page_title,
+ 'L_ICON' => $user->lang['PM_ICON'],
+ 'L_MESSAGE_BODY_EXPLAIN' => (intval($config['max_post_chars'])) ? sprintf($user->lang['MESSAGE_BODY_EXPLAIN'], intval($config['max_post_chars'])) : '',
+
+ 'SUBJECT' => (isset($message_subject)) ? $message_subject : '',
+ 'MESSAGE' => $message_text,
+ 'BBCODE_STATUS' => ($bbcode_status) ? sprintf($user->lang['BBCODE_IS_ON'], '<a href="' . append_sid("{$phpbb_root_path}faq.$phpEx", 'mode=bbcode') . '">', '</a>') : sprintf($user->lang['BBCODE_IS_OFF'], '<a href="' . append_sid("{$phpbb_root_path}faq.$phpEx", 'mode=bbcode') . '">', '</a>'),
+ 'IMG_STATUS' => ($img_status) ? $user->lang['IMAGES_ARE_ON'] : $user->lang['IMAGES_ARE_OFF'],
+ 'FLASH_STATUS' => ($flash_status) ? $user->lang['FLASH_IS_ON'] : $user->lang['FLASH_IS_OFF'],
+ 'SMILIES_STATUS' => ($smilies_status) ? $user->lang['SMILIES_ARE_ON'] : $user->lang['SMILIES_ARE_OFF'],
+ 'URL_STATUS' => ($url_status) ? $user->lang['URL_IS_ON'] : $user->lang['URL_IS_OFF'],
+ 'MAX_FONT_SIZE' => (int) $config['max_post_font_size'],
+ 'MINI_POST_IMG' => $user->img('icon_post_target', $user->lang['PM']),
+ 'ERROR' => (sizeof($error)) ? implode('<br />', $error) : '',
+ 'MAX_RECIPIENTS' => ($config['allow_mass_pm'] && ($auth->acl_get('u_masspm') || $auth->acl_get('u_masspm_group'))) ? $max_recipients : 0,
+
+ 'S_COMPOSE_PM' => true,
+ 'S_EDIT_POST' => ($action == 'edit'),
+ 'S_SHOW_PM_ICONS' => $s_pm_icons,
+ 'S_BBCODE_ALLOWED' => $bbcode_status,
+ 'S_BBCODE_CHECKED' => ($bbcode_checked) ? ' checked="checked"' : '',
+ 'S_SMILIES_ALLOWED' => $smilies_status,
+ 'S_SMILIES_CHECKED' => ($smilies_checked) ? ' checked="checked"' : '',
+ 'S_SIG_ALLOWED' => ($config['allow_sig'] && $config['allow_sig_pm'] && $auth->acl_get('u_sig')),
+ 'S_SIGNATURE_CHECKED' => ($sig_checked) ? ' checked="checked"' : '',
+ 'S_LINKS_ALLOWED' => $url_status,
+ 'S_MAGIC_URL_CHECKED' => ($urls_checked) ? ' checked="checked"' : '',
+ 'S_SAVE_ALLOWED' => ($auth->acl_get('u_savedrafts') && $action != 'edit') ? true : false,
+ 'S_HAS_DRAFTS' => ($auth->acl_get('u_savedrafts') && $drafts),
+ 'S_FORM_ENCTYPE' => $form_enctype,
+
+ 'S_BBCODE_IMG' => $img_status,
+ 'S_BBCODE_FLASH' => $flash_status,
+ 'S_BBCODE_QUOTE' => true,
+ 'S_BBCODE_URL' => $url_status,
+
+ 'S_POST_ACTION' => $s_action,
+ 'S_HIDDEN_ADDRESS_FIELD' => $s_hidden_address_field,
+ 'S_HIDDEN_FIELDS' => $s_hidden_fields,
+
+ 'S_CLOSE_PROGRESS_WINDOW' => isset($_POST['add_file']),
+ 'U_PROGRESS_BAR' => append_sid("{$phpbb_root_path}posting.$phpEx", 'f=0&amp;mode=popup'),
+ 'UA_PROGRESS_BAR' => addslashes(append_sid("{$phpbb_root_path}posting.$phpEx", 'f=0&amp;mode=popup')),
+ ));
+
+ // Build custom bbcodes array
+ display_custom_bbcodes();
+
+ // Show attachment box for adding attachments if true
+ $allowed = ($auth->acl_get('u_pm_attach') && $config['allow_pm_attach'] && $form_enctype);
+
+ // Attachment entry
+ posting_gen_attachment_entry($attachment_data, $filename_data, $allowed);
+
+ // Message History
+ if ($action == 'reply' || $action == 'quote' || $action == 'forward')
+ {
+ if (message_history($msg_id, $user->data['user_id'], $post, array(), true))
+ {
+ $template->assign_var('S_DISPLAY_HISTORY', true);
+ }
+ }
+}
+
+/**
+* For composing messages, handle list actions
+*/
+function handle_message_list_actions(&$address_list, &$error, $remove_u, $remove_g, $add_to, $add_bcc)
+{
+ global $auth, $db, $user;
+
+ // Delete User [TO/BCC]
+ if ($remove_u && !empty($_REQUEST['remove_u']) && is_array($_REQUEST['remove_u']))
+ {
+ $remove_user_id = array_keys($_REQUEST['remove_u']);
+
+ if (isset($remove_user_id[0]))
+ {
+ unset($address_list['u'][(int) $remove_user_id[0]]);
+ }
+ }
+
+ // Delete Group [TO/BCC]
+ if ($remove_g && !empty($_REQUEST['remove_g']) && is_array($_REQUEST['remove_g']))
+ {
+ $remove_group_id = array_keys($_REQUEST['remove_g']);
+
+ if (isset($remove_group_id[0]))
+ {
+ unset($address_list['g'][(int) $remove_group_id[0]]);
+ }
+ }
+
+ // Add Selected Groups
+ $group_list = request_var('group_list', array(0));
+
+ // Build usernames to add
+ $usernames = request_var('username', '', true);
+ $usernames = (empty($usernames)) ? array() : array($usernames);
+
+ $username_list = request_var('username_list', '', true);
+ if ($username_list)
+ {
+ $usernames = array_merge($usernames, explode("\n", $username_list));
+ }
+
+ // If add to or add bcc not pressed, users could still have usernames listed they want to add...
+ if (!$add_to && !$add_bcc && (sizeof($group_list) || sizeof($usernames)))
+ {
+ $add_to = true;
+
+ global $refresh, $submit, $preview;
+
+ $refresh = true;
+ $submit = false;
+
+ // Preview is only true if there was also a message entered
+ if (request_var('message', ''))
+ {
+ $preview = true;
+ }
+ }
+
+ // Add User/Group [TO]
+ if ($add_to || $add_bcc)
+ {
+ $type = ($add_to) ? 'to' : 'bcc';
+
+ if (sizeof($group_list))
+ {
+ foreach ($group_list as $group_id)
+ {
+ $address_list['g'][$group_id] = $type;
+ }
+ }
+
+ // User ID's to add...
+ $user_id_ary = array();
+
+ // Reveal the correct user_ids
+ if (sizeof($usernames))
+ {
+ $user_id_ary = array();
+ user_get_id_name($user_id_ary, $usernames, array(USER_NORMAL, USER_FOUNDER, USER_INACTIVE));
+
+ // If there are users not existing, we will at least print a notice...
+ if (!sizeof($user_id_ary))
+ {
+ $error[] = $user->lang['PM_NO_USERS'];
+ }
+ }
+
+ // Add Friends if specified
+ $friend_list = (isset($_REQUEST['add_' . $type]) && is_array($_REQUEST['add_' . $type])) ? array_map('intval', array_keys($_REQUEST['add_' . $type])) : array();
+ $user_id_ary = array_merge($user_id_ary, $friend_list);
+
+ foreach ($user_id_ary as $user_id)
+ {
+ if ($user_id == ANONYMOUS)
+ {
+ continue;
+ }
+
+ $address_list['u'][$user_id] = $type;
+ }
+ }
+
+ // Check for disallowed recipients
+ if (!empty($address_list['u']))
+ {
+ // We need to check their PM status (do they want to receive PM's?)
+ // Only check if not a moderator or admin, since they are allowed to override this user setting
+ if (!$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_'))
+ {
+ $sql = 'SELECT user_id
+ FROM ' . USERS_TABLE . '
+ WHERE ' . $db->sql_in_set('user_id', array_keys($address_list['u'])) . '
+ AND user_allow_pm = 0';
+ $result = $db->sql_query($sql);
+
+ $removed = false;
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $removed = true;
+ unset($address_list['u'][$row['user_id']]);
+ }
+ $db->sql_freeresult($result);
+
+ // print a notice about users not being added who do not want to receive pms
+ if ($removed)
+ {
+ $error[] = $user->lang['PM_USERS_REMOVED_NO_PM'];
+ }
+ }
+ }
+}
+
+/**
+* Build the hidden field for the recipients. Needed, as the variable is not read via request_var.
+*/
+function build_address_field($address_list)
+{
+ $s_hidden_address_field = '';
+ foreach ($address_list as $type => $adr_ary)
+ {
+ foreach ($adr_ary as $id => $field)
+ {
+ $s_hidden_address_field .= '<input type="hidden" name="address_list[' . (($type == 'u') ? 'u' : 'g') . '][' . (int) $id . ']" value="' . (($field == 'to') ? 'to' : 'bcc') . '" />';
+ }
+ }
+ return $s_hidden_address_field;
+}
+
+/**
+* Return number of private message recipients
+*/
+function num_recipients($address_list)
+{
+ $num_recipients = 0;
+
+ foreach ($address_list as $field => $adr_ary)
+ {
+ $num_recipients += sizeof($adr_ary);
+ }
+
+ return $num_recipients;
+}
+
+/**
+* Get number of 'num_recipients' recipients from first position
+*/
+function get_recipients($address_list, $num_recipients = 1)
+{
+ $recipient = array();
+
+ $count = 0;
+ foreach ($address_list as $field => $adr_ary)
+ {
+ foreach ($adr_ary as $id => $type)
+ {
+ if ($count >= $num_recipients)
+ {
+ break 2;
+ }
+ $recipient[$field][$id] = $type;
+ $count++;
+ }
+ }
+
+ return $recipient;
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/ucp_pm_options.php b/phpBB/includes/ucp/ucp_pm_options.php
new file mode 100644
index 0000000000..e80c0672cf
--- /dev/null
+++ b/phpBB/includes/ucp/ucp_pm_options.php
@@ -0,0 +1,836 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* Execute message options
+*/
+function message_options($id, $mode, $global_privmsgs_rules, $global_rule_conditions)
+{
+ global $phpbb_root_path, $phpEx, $user, $template, $auth, $config, $db;
+
+ $redirect_url = append_sid("{$phpbb_root_path}ucp.$phpEx", "i=pm&amp;mode=options");
+
+ add_form_key('ucp_pm_options');
+ // Change "full folder" setting - what to do if folder is full
+ if (isset($_POST['fullfolder']))
+ {
+ check_form_key('ucp_pm_options', $config['form_token_lifetime'], $redirect_url);
+ $full_action = request_var('full_action', 0);
+
+ $set_folder_id = 0;
+ switch ($full_action)
+ {
+ case 1:
+ $set_folder_id = FULL_FOLDER_DELETE;
+ break;
+
+ case 2:
+ $set_folder_id = request_var('full_move_to', PRIVMSGS_INBOX);
+ break;
+
+ case 3:
+ $set_folder_id = FULL_FOLDER_HOLD;
+ break;
+
+ default:
+ $full_action = 0;
+ break;
+ }
+
+ if ($full_action)
+ {
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET user_full_folder = ' . $set_folder_id . '
+ WHERE user_id = ' . $user->data['user_id'];
+ $db->sql_query($sql);
+
+ $user->data['user_full_folder'] = $set_folder_id;
+
+ $message = $user->lang['FULL_FOLDER_OPTION_CHANGED'] . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $redirect_url . '">', '</a>');
+ meta_refresh(3, $redirect_url);
+ trigger_error($message);
+ }
+ }
+
+ // Add Folder
+ if (isset($_POST['addfolder']))
+ {
+ if (check_form_key('ucp_pm_options'))
+ {
+ $folder_name = utf8_normalize_nfc(request_var('foldername', '', true));
+ $msg = '';
+
+ if ($folder_name)
+ {
+ $sql = 'SELECT folder_name
+ FROM ' . PRIVMSGS_FOLDER_TABLE . "
+ WHERE folder_name = '" . $db->sql_escape($folder_name) . "'
+ AND user_id = " . $user->data['user_id'];
+ $result = $db->sql_query_limit($sql, 1);
+ $row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if ($row)
+ {
+ trigger_error(sprintf($user->lang['FOLDER_NAME_EXIST'], $folder_name));
+ }
+
+ $sql = 'SELECT COUNT(folder_id) as num_folder
+ FROM ' . PRIVMSGS_FOLDER_TABLE . '
+ WHERE user_id = ' . $user->data['user_id'];
+ $result = $db->sql_query($sql);
+ $num_folder = (int) $db->sql_fetchfield('num_folder');
+ $db->sql_freeresult($result);
+
+ if ($num_folder >= $config['pm_max_boxes'])
+ {
+ trigger_error('MAX_FOLDER_REACHED');
+ }
+
+ $sql = 'INSERT INTO ' . PRIVMSGS_FOLDER_TABLE . ' ' . $db->sql_build_array('INSERT', array(
+ 'user_id' => (int) $user->data['user_id'],
+ 'folder_name' => $folder_name)
+ );
+ $db->sql_query($sql);
+ $msg = $user->lang['FOLDER_ADDED'];
+ }
+ else
+ {
+ $msg = $user->lang['FOLDER_NAME_EMPTY'];
+ }
+ }
+ else
+ {
+ $msg = $user->lang['FORM_INVALID'];
+ }
+ $message = $msg . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $redirect_url . '">', '</a>');
+ meta_refresh(3, $redirect_url);
+ trigger_error($message);
+ }
+
+ // Rename folder
+ if (isset($_POST['rename_folder']))
+ {
+ if (check_form_key('ucp_pm_options'))
+ {
+ $new_folder_name = utf8_normalize_nfc(request_var('new_folder_name', '', true));
+ $rename_folder_id= request_var('rename_folder_id', 0);
+
+ if (!$new_folder_name)
+ {
+ trigger_error('NO_NEW_FOLDER_NAME');
+ }
+
+ // Select custom folder
+ $sql = 'SELECT folder_name, pm_count
+ FROM ' . PRIVMSGS_FOLDER_TABLE . "
+ WHERE user_id = {$user->data['user_id']}
+ AND folder_id = $rename_folder_id";
+ $result = $db->sql_query_limit($sql, 1);
+ $folder_row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if (!$folder_row)
+ {
+ trigger_error('CANNOT_RENAME_FOLDER');
+ }
+
+ $sql = 'UPDATE ' . PRIVMSGS_FOLDER_TABLE . "
+ SET folder_name = '" . $db->sql_escape($new_folder_name) . "'
+ WHERE folder_id = $rename_folder_id
+ AND user_id = {$user->data['user_id']}";
+ $db->sql_query($sql);
+ $msg = $user->lang['FOLDER_RENAMED'];
+ }
+ else
+ {
+ $msg = $user->lang['FORM_INVALID'];
+ }
+
+ $message = $msg . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $redirect_url . '">', '</a>');
+
+ meta_refresh(3, $redirect_url);
+ trigger_error($message);
+ }
+
+ // Remove Folder
+ if (isset($_POST['remove_folder']))
+ {
+ $remove_folder_id = request_var('remove_folder_id', 0);
+
+ // Default to "move all messages to inbox"
+ $remove_action = request_var('remove_action', 1);
+ $move_to = request_var('move_to', PRIVMSGS_INBOX);
+
+ // Move to same folder?
+ if ($remove_action == 1 && $remove_folder_id == $move_to)
+ {
+ trigger_error('CANNOT_MOVE_TO_SAME_FOLDER');
+ }
+
+ // Select custom folder
+ $sql = 'SELECT folder_name, pm_count
+ FROM ' . PRIVMSGS_FOLDER_TABLE . "
+ WHERE user_id = {$user->data['user_id']}
+ AND folder_id = $remove_folder_id";
+ $result = $db->sql_query_limit($sql, 1);
+ $folder_row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if (!$folder_row)
+ {
+ trigger_error('CANNOT_REMOVE_FOLDER');
+ }
+
+ $s_hidden_fields = array(
+ 'remove_folder_id' => $remove_folder_id,
+ 'remove_action' => $remove_action,
+ 'move_to' => $move_to,
+ 'remove_folder' => 1
+ );
+
+ // Do we need to confirm?
+ if (confirm_box(true))
+ {
+ // Gather message ids
+ $sql = 'SELECT msg_id
+ FROM ' . PRIVMSGS_TO_TABLE . '
+ WHERE user_id = ' . $user->data['user_id'] . "
+ AND folder_id = $remove_folder_id";
+ $result = $db->sql_query($sql);
+
+ $msg_ids = array();
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $msg_ids[] = (int) $row['msg_id'];
+ }
+ $db->sql_freeresult($result);
+
+ // First of all, copy all messages to another folder... or delete all messages
+ switch ($remove_action)
+ {
+ // Move Messages
+ case 1:
+ $num_moved = move_pm($user->data['user_id'], $user->data['message_limit'], $msg_ids, $move_to, $remove_folder_id);
+
+ // Something went wrong, only partially moved?
+ if ($num_moved != $folder_row['pm_count'])
+ {
+ trigger_error(sprintf($user->lang['MOVE_PM_ERROR'], $num_moved, $folder_row['pm_count']));
+ }
+ break;
+
+ // Remove Messages
+ case 2:
+ delete_pm($user->data['user_id'], $msg_ids, $remove_folder_id);
+ break;
+ }
+
+ // Remove folder
+ $sql = 'DELETE FROM ' . PRIVMSGS_FOLDER_TABLE . "
+ WHERE user_id = {$user->data['user_id']}
+ AND folder_id = $remove_folder_id";
+ $db->sql_query($sql);
+
+ // Check full folder option. If the removed folder has been specified as destination switch back to inbox
+ if ($user->data['user_full_folder'] == $remove_folder_id)
+ {
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET user_full_folder = ' . PRIVMSGS_INBOX . '
+ WHERE user_id = ' . $user->data['user_id'];
+ $db->sql_query($sql);
+
+ $user->data['user_full_folder'] = PRIVMSGS_INBOX;
+ }
+
+ // Now make sure the folder is not used for rules
+ // We assign another folder id (the one the messages got moved to) or assign the INBOX (to not have to remove any rule)
+ $sql = 'UPDATE ' . PRIVMSGS_RULES_TABLE . ' SET rule_folder_id = ';
+ $sql .= ($remove_action == 1) ? $move_to : PRIVMSGS_INBOX;
+ $sql .= ' WHERE rule_folder_id = ' . $remove_folder_id;
+
+ $db->sql_query($sql);
+
+ $meta_info = append_sid("{$phpbb_root_path}ucp.$phpEx", "i=pm&amp;mode=$mode");
+ $message = $user->lang['FOLDER_REMOVED'];
+
+ meta_refresh(3, $meta_info);
+ $message .= '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $meta_info . '">', '</a>');
+ trigger_error($message);
+ }
+ else
+ {
+ confirm_box(false, 'REMOVE_FOLDER', build_hidden_fields($s_hidden_fields));
+ }
+ }
+
+ // Add Rule
+ if (isset($_POST['add_rule']))
+ {
+ if (check_form_key('ucp_pm_options'))
+ {
+ $check_option = request_var('check_option', 0);
+ $rule_option = request_var('rule_option', 0);
+ $cond_option = request_var('cond_option', '');
+ $action_option = explode('|', request_var('action_option', ''));
+ $rule_string = ($cond_option != 'none') ? utf8_normalize_nfc(request_var('rule_string', '', true)) : '';
+ $rule_user_id = ($cond_option != 'none') ? request_var('rule_user_id', 0) : 0;
+ $rule_group_id = ($cond_option != 'none') ? request_var('rule_group_id', 0) : 0;
+
+ $action = (int) $action_option[0];
+ $folder_id = (int) $action_option[1];
+
+ if (!$action || !$check_option || !$rule_option || !$cond_option || ($cond_option != 'none' && !$rule_string))
+ {
+ trigger_error('RULE_NOT_DEFINED');
+ }
+
+ if (($cond_option == 'user' && !$rule_user_id) || ($cond_option == 'group' && !$rule_group_id))
+ {
+ trigger_error('RULE_NOT_DEFINED');
+ }
+
+ $rule_ary = array(
+ 'user_id' => $user->data['user_id'],
+ 'rule_check' => $check_option,
+ 'rule_connection' => $rule_option,
+ 'rule_string' => $rule_string,
+ 'rule_user_id' => $rule_user_id,
+ 'rule_group_id' => $rule_group_id,
+ 'rule_action' => $action,
+ 'rule_folder_id' => $folder_id
+ );
+
+ $sql = 'SELECT rule_id
+ FROM ' . PRIVMSGS_RULES_TABLE . '
+ WHERE ' . $db->sql_build_array('SELECT', $rule_ary);
+ $result = $db->sql_query($sql);
+ $row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if ($row)
+ {
+ trigger_error('RULE_ALREADY_DEFINED');
+ }
+
+ $sql = 'INSERT INTO ' . PRIVMSGS_RULES_TABLE . ' ' . $db->sql_build_array('INSERT', $rule_ary);
+ $db->sql_query($sql);
+
+ // Update users message rules
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET user_message_rules = 1
+ WHERE user_id = ' . $user->data['user_id'];
+ $db->sql_query($sql);
+
+ $msg = $user->lang['RULE_ADDED'];
+ }
+ else
+ {
+ $msg = $user->lang['FORM_INVALID'];
+ }
+ $message = $msg . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $redirect_url . '">', '</a>');
+ meta_refresh(3, $redirect_url);
+ trigger_error($message);
+ }
+
+ // Remove Rule
+ if (isset($_POST['delete_rule']) && !isset($_POST['cancel']))
+ {
+ $delete_id = array_keys(request_var('delete_rule', array(0 => 0)));
+ $delete_id = (!empty($delete_id[0])) ? $delete_id[0] : 0;
+
+ if (!$delete_id)
+ {
+ redirect(append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;mode=' . $mode));
+ }
+
+ // Do we need to confirm?
+ if (confirm_box(true))
+ {
+ $sql = 'DELETE FROM ' . PRIVMSGS_RULES_TABLE . '
+ WHERE user_id = ' . $user->data['user_id'] . "
+ AND rule_id = $delete_id";
+ $db->sql_query($sql);
+
+ $meta_info = append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;mode=' . $mode);
+ $message = $user->lang['RULE_DELETED'];
+
+ // Reset user_message_rules if no more assigned
+ $sql = 'SELECT rule_id
+ FROM ' . PRIVMSGS_RULES_TABLE . '
+ WHERE user_id = ' . $user->data['user_id'];
+ $result = $db->sql_query_limit($sql, 1);
+ $row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ // Update users message rules
+ if (!$row)
+ {
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET user_message_rules = 0
+ WHERE user_id = ' . $user->data['user_id'];
+ $db->sql_query($sql);
+ }
+
+ meta_refresh(3, $meta_info);
+ $message .= '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $meta_info . '">', '</a>');
+ trigger_error($message);
+ }
+ else
+ {
+ confirm_box(false, 'DELETE_RULE', build_hidden_fields(array('delete_rule' => array($delete_id => 1))));
+ }
+ }
+
+ $folder = array();
+
+ $sql = 'SELECT COUNT(msg_id) as num_messages
+ FROM ' . PRIVMSGS_TO_TABLE . '
+ WHERE user_id = ' . $user->data['user_id'] . '
+ AND folder_id = ' . PRIVMSGS_INBOX;
+ $result = $db->sql_query($sql);
+ $num_messages = (int) $db->sql_fetchfield('num_messages');
+ $db->sql_freeresult($result);
+
+ $folder[PRIVMSGS_INBOX] = array(
+ 'folder_name' => $user->lang['PM_INBOX'],
+ 'message_status' => sprintf($user->lang['FOLDER_MESSAGE_STATUS'], $num_messages, $user->data['message_limit'])
+ );
+
+ $sql = 'SELECT folder_id, folder_name, pm_count
+ FROM ' . PRIVMSGS_FOLDER_TABLE . '
+ WHERE user_id = ' . $user->data['user_id'];
+ $result = $db->sql_query($sql);
+
+ $num_user_folder = 0;
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $num_user_folder++;
+ $folder[$row['folder_id']] = array(
+ 'folder_name' => $row['folder_name'],
+ 'message_status' => sprintf($user->lang['FOLDER_MESSAGE_STATUS'], $row['pm_count'], $user->data['message_limit'])
+ );
+ }
+ $db->sql_freeresult($result);
+
+ $s_full_folder_options = $s_to_folder_options = $s_folder_options = '';
+
+ if ($user->data['user_full_folder'] == FULL_FOLDER_NONE)
+ {
+ // -3 here to let the correct folder id be selected
+ $to_folder_id = $config['full_folder_action'] - 3;
+ }
+ else
+ {
+ $to_folder_id = $user->data['user_full_folder'];
+ }
+
+ foreach ($folder as $folder_id => $folder_ary)
+ {
+ $s_full_folder_options .= '<option value="' . $folder_id . '"' . (($user->data['user_full_folder'] == $folder_id) ? ' selected="selected"' : '') . '>' . $folder_ary['folder_name'] . ' (' . $folder_ary['message_status'] . ')</option>';
+ $s_to_folder_options .= '<option value="' . $folder_id . '"' . (($to_folder_id == $folder_id) ? ' selected="selected"' : '') . '>' . $folder_ary['folder_name'] . ' (' . $folder_ary['message_status'] . ')</option>';
+
+ if ($folder_id != PRIVMSGS_INBOX)
+ {
+ $s_folder_options .= '<option value="' . $folder_id . '">' . $folder_ary['folder_name'] . ' (' . $folder_ary['message_status'] . ')</option>';
+ }
+ }
+
+ $s_delete_checked = ($user->data['user_full_folder'] == FULL_FOLDER_DELETE) ? ' checked="checked"' : '';
+ $s_hold_checked = ($user->data['user_full_folder'] == FULL_FOLDER_HOLD) ? ' checked="checked"' : '';
+ $s_move_checked = ($user->data['user_full_folder'] >= 0) ? ' checked="checked"' : '';
+
+ if ($user->data['user_full_folder'] == FULL_FOLDER_NONE)
+ {
+ switch ($config['full_folder_action'])
+ {
+ case 1:
+ $s_delete_checked = ' checked="checked"';
+ break;
+
+ case 2:
+ $s_hold_checked = ' checked="checked"';
+ break;
+ }
+ }
+
+ $template->assign_vars(array(
+ 'S_FULL_FOLDER_OPTIONS' => $s_full_folder_options,
+ 'S_TO_FOLDER_OPTIONS' => $s_to_folder_options,
+ 'S_FOLDER_OPTIONS' => $s_folder_options,
+ 'S_DELETE_CHECKED' => $s_delete_checked,
+ 'S_HOLD_CHECKED' => $s_hold_checked,
+ 'S_MOVE_CHECKED' => $s_move_checked,
+ 'S_MAX_FOLDER_REACHED' => ($num_user_folder >= $config['pm_max_boxes']) ? true : false,
+ 'S_MAX_FOLDER_ZERO' => ($config['pm_max_boxes'] == 0) ? true : false,
+
+ 'DEFAULT_ACTION' => ($config['full_folder_action'] == 1) ? $user->lang['DELETE_OLDEST_MESSAGES'] : $user->lang['HOLD_NEW_MESSAGES'],
+
+ 'U_FIND_USERNAME' => append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=searchuser&amp;form=ucp&amp;field=rule_string&amp;select_single=true'),
+ ));
+
+ $rule_lang = $action_lang = $check_lang = array();
+
+ // Build all three language arrays
+ preg_replace('#^((RULE|ACTION|CHECK)_([A-Z0-9_]+))$#e', "\${strtolower('\\2') . '_lang'}[constant('\\1')] = \$user->lang['PM_\\2']['\\3']", array_keys(get_defined_constants()));
+
+ /*
+ Rule Ordering:
+ -> CHECK_* -> RULE_* [IN $global_privmsgs_rules:CHECK_*] -> [IF $rule_conditions[RULE_*] [|text|bool|user|group|own_group]] -> ACTION_*
+ */
+
+ $check_option = request_var('check_option', 0);
+ $rule_option = request_var('rule_option', 0);
+ $cond_option = request_var('cond_option', '');
+ $action_option = request_var('action_option', '');
+ $back = (isset($_REQUEST['back'])) ? request_var('back', array('' => 0)) : array();
+
+ if (sizeof($back))
+ {
+ if ($action_option)
+ {
+ $action_option = '';
+ }
+ else if ($cond_option)
+ {
+ $cond_option = '';
+ }
+ else if ($rule_option)
+ {
+ $rule_option = 0;
+ }
+ else if ($check_option)
+ {
+ $check_option = 0;
+ }
+ }
+
+ if (isset($back['action']) && $cond_option == 'none')
+ {
+ $back['cond'] = true;
+ }
+
+ // Check
+ if (!isset($global_privmsgs_rules[$check_option]))
+ {
+ $check_option = 0;
+ }
+
+ define_check_option(($check_option && !isset($back['rule'])) ? true : false, $check_option, $check_lang);
+
+ if ($check_option && !isset($back['rule']))
+ {
+ define_rule_option(($rule_option && !isset($back['cond'])) ? true : false, $rule_option, $rule_lang, $global_privmsgs_rules[$check_option]);
+ }
+
+ if ($rule_option && !isset($back['cond']))
+ {
+ if (!isset($global_rule_conditions[$rule_option]))
+ {
+ $cond_option = 'none';
+ $template->assign_var('NONE_CONDITION', true);
+ }
+ else
+ {
+ define_cond_option(($cond_option && !isset($back['action'])) ? true : false, $cond_option, $rule_option, $global_rule_conditions);
+ }
+ }
+
+ if ($cond_option && !isset($back['action']))
+ {
+ define_action_option(false, $action_option, $action_lang, $folder);
+ }
+
+ show_defined_rules($user->data['user_id'], $check_lang, $rule_lang, $action_lang, $folder);
+}
+
+/**
+* Defining check option for message rules
+*/
+function define_check_option($hardcoded, $check_option, $check_lang)
+{
+ global $template;
+
+ $s_check_options = '';
+ if (!$hardcoded)
+ {
+ foreach ($check_lang as $value => $lang)
+ {
+ $s_check_options .= '<option value="' . $value . '"' . (($value == $check_option) ? ' selected="selected"' : '') . '>' . $lang . '</option>';
+ }
+ }
+
+ $template->assign_vars(array(
+ 'S_CHECK_DEFINED' => true,
+ 'S_CHECK_SELECT' => ($hardcoded) ? false : true,
+ 'CHECK_CURRENT' => isset($check_lang[$check_option]) ? $check_lang[$check_option] : '',
+ 'S_CHECK_OPTIONS' => $s_check_options,
+ 'CHECK_OPTION' => $check_option)
+ );
+}
+
+/**
+* Defining action option for message rules
+*/
+function define_action_option($hardcoded, $action_option, $action_lang, $folder)
+{
+ global $db, $template, $user;
+
+ $l_action = $s_action_options = '';
+ if ($hardcoded)
+ {
+ $option = explode('|', $action_option);
+ $action = (int) $option[0];
+ $folder_id = (int) $option[1];
+
+ $l_action = $action_lang[$action];
+ if ($action == ACTION_PLACE_INTO_FOLDER)
+ {
+ $l_action .= ' -> ' . $folder[$folder_id]['folder_name'];
+ }
+ }
+ else
+ {
+ foreach ($action_lang as $action => $lang)
+ {
+ if ($action == ACTION_PLACE_INTO_FOLDER)
+ {
+ foreach ($folder as $folder_id => $folder_ary)
+ {
+ $s_action_options .= '<option value="' . $action . '|' . $folder_id . '"' . (($action_option == $action . '|' . $folder_id) ? ' selected="selected"' : '') . '>' . $lang . ' -> ' . $folder_ary['folder_name'] . '</option>';
+ }
+ }
+ else
+ {
+ $s_action_options .= '<option value="' . $action . '|0"' . (($action_option == $action . '|0') ? ' selected="selected"' : '') . '>' . $lang . '</option>';
+ }
+ }
+ }
+
+ $template->assign_vars(array(
+ 'S_ACTION_DEFINED' => true,
+ 'S_ACTION_SELECT' => ($hardcoded) ? false : true,
+ 'ACTION_CURRENT' => $l_action,
+ 'S_ACTION_OPTIONS' => $s_action_options,
+ 'ACTION_OPTION' => $action_option)
+ );
+}
+
+/**
+* Defining rule option for message rules
+*/
+function define_rule_option($hardcoded, $rule_option, $rule_lang, $check_ary)
+{
+ global $template;
+
+ $s_rule_options = '';
+ if (!$hardcoded)
+ {
+ foreach ($check_ary as $value => $_check)
+ {
+ $s_rule_options .= '<option value="' . $value . '"' . (($value == $rule_option) ? ' selected="selected"' : '') . '>' . $rule_lang[$value] . '</option>';
+ }
+ }
+
+ $template->assign_vars(array(
+ 'S_RULE_DEFINED' => true,
+ 'S_RULE_SELECT' => !$hardcoded,
+ 'RULE_CURRENT' => isset($rule_lang[$rule_option]) ? $rule_lang[$rule_option] : '',
+ 'S_RULE_OPTIONS' => $s_rule_options,
+ 'RULE_OPTION' => $rule_option)
+ );
+}
+
+/**
+* Defining condition option for message rules
+*/
+function define_cond_option($hardcoded, $cond_option, $rule_option, $global_rule_conditions)
+{
+ global $db, $template, $auth, $user;
+
+ $template->assign_vars(array(
+ 'S_COND_DEFINED' => true,
+ 'S_COND_SELECT' => (!$hardcoded && isset($global_rule_conditions[$rule_option])) ? true : false)
+ );
+
+ // Define COND_OPTION
+ if (!isset($global_rule_conditions[$rule_option]))
+ {
+ $template->assign_vars(array(
+ 'COND_OPTION' => 'none',
+ 'COND_CURRENT' => false)
+ );
+ return;
+ }
+
+ // Define Condition
+ $condition = $global_rule_conditions[$rule_option];
+ $current_value = '';
+
+ switch ($condition)
+ {
+ case 'text':
+ $rule_string = utf8_normalize_nfc(request_var('rule_string', '', true));
+
+ $template->assign_vars(array(
+ 'S_TEXT_CONDITION' => true,
+ 'CURRENT_STRING' => $rule_string,
+ 'CURRENT_USER_ID' => 0,
+ 'CURRENT_GROUP_ID' => 0)
+ );
+
+ $current_value = $rule_string;
+ break;
+
+ case 'user':
+ $rule_user_id = request_var('rule_user_id', 0);
+ $rule_string = utf8_normalize_nfc(request_var('rule_string', '', true));
+
+ if ($rule_string && !$rule_user_id)
+ {
+ $sql = 'SELECT user_id
+ FROM ' . USERS_TABLE . "
+ WHERE username_clean = '" . $db->sql_escape(utf8_clean_string($rule_string)) . "'";
+ $result = $db->sql_query($sql);
+ $rule_user_id = (int) $db->sql_fetchfield('user_id');
+ $db->sql_freeresult($result);
+
+ if (!$rule_user_id)
+ {
+ $rule_string = '';
+ }
+ }
+ else if (!$rule_string && $rule_user_id)
+ {
+ $sql = 'SELECT username
+ FROM ' . USERS_TABLE . "
+ WHERE user_id = $rule_user_id";
+ $result = $db->sql_query($sql);
+ $rule_string = $db->sql_fetchfield('username');
+ $db->sql_freeresult($result);
+
+ if (!$rule_string)
+ {
+ $rule_user_id = 0;
+ }
+ }
+
+ $template->assign_vars(array(
+ 'S_USER_CONDITION' => true,
+ 'CURRENT_STRING' => $rule_string,
+ 'CURRENT_USER_ID' => $rule_user_id,
+ 'CURRENT_GROUP_ID' => 0)
+ );
+
+ $current_value = $rule_string;
+ break;
+
+ case 'group':
+ $rule_group_id = request_var('rule_group_id', 0);
+ $rule_string = utf8_normalize_nfc(request_var('rule_string', '', true));
+
+ $sql = 'SELECT g.group_id, g.group_name, g.group_type
+ FROM ' . GROUPS_TABLE . ' g ';
+
+ if (!$auth->acl_gets('a_group', 'a_groupadd', 'a_groupdel'))
+ {
+ $sql .= 'LEFT JOIN ' . USER_GROUP_TABLE . ' ug
+ ON (
+ g.group_id = ug.group_id
+ AND ug.user_id = ' . $user->data['user_id'] . '
+ AND ug.user_pending = 0
+ )
+ WHERE (ug.user_id = ' . $user->data['user_id'] . ' OR g.group_type <> ' . GROUP_HIDDEN . ')
+ AND';
+ }
+ else
+ {
+ $sql .= 'WHERE';
+ }
+
+ $sql .= " (g.group_name NOT IN ('GUESTS', 'BOTS') OR g.group_type <> " . GROUP_SPECIAL . ')
+ ORDER BY g.group_type DESC, g.group_name ASC';
+
+ $result = $db->sql_query($sql);
+
+ $s_group_options = '';
+ while ($row = $db->sql_fetchrow($result))
+ {
+ if ($rule_group_id && ($row['group_id'] == $rule_group_id))
+ {
+ $rule_string = (($row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $row['group_name']] : $row['group_name']);
+ }
+
+ $s_class = ($row['group_type'] == GROUP_SPECIAL) ? ' class="sep"' : '';
+ $s_selected = ($row['group_id'] == $rule_group_id) ? ' selected="selected"' : '';
+
+ $s_group_options .= '<option value="' . $row['group_id'] . '"' . $s_class . $s_selected . '>' . (($row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $row['group_name']] : $row['group_name']) . '</option>';
+ }
+ $db->sql_freeresult($result);
+
+ $template->assign_vars(array(
+ 'S_GROUP_CONDITION' => true,
+ 'S_GROUP_OPTIONS' => $s_group_options,
+ 'CURRENT_STRING' => $rule_string,
+ 'CURRENT_USER_ID' => 0,
+ 'CURRENT_GROUP_ID' => $rule_group_id)
+ );
+
+ $current_value = $rule_string;
+ break;
+
+ default:
+ return;
+ }
+
+ $template->assign_vars(array(
+ 'COND_OPTION' => $condition,
+ 'COND_CURRENT' => $current_value)
+ );
+}
+
+/**
+* Display defined message rules
+*/
+function show_defined_rules($user_id, $check_lang, $rule_lang, $action_lang, $folder)
+{
+ global $db, $template;
+
+ $sql = 'SELECT *
+ FROM ' . PRIVMSGS_RULES_TABLE . '
+ WHERE user_id = ' . $user_id . '
+ ORDER BY rule_id ASC';
+ $result = $db->sql_query($sql);
+
+ $count = 0;
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $template->assign_block_vars('rule', array(
+ 'COUNT' => ++$count,
+ 'RULE_ID' => $row['rule_id'],
+ 'CHECK' => $check_lang[$row['rule_check']],
+ 'RULE' => $rule_lang[$row['rule_connection']],
+ 'STRING' => $row['rule_string'],
+ 'ACTION' => $action_lang[$row['rule_action']],
+ 'FOLDER' => ($row['rule_action'] == ACTION_PLACE_INTO_FOLDER) ? $folder[$row['rule_folder_id']]['folder_name'] : '')
+ );
+ }
+ $db->sql_freeresult($result);
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/ucp_pm_viewfolder.php b/phpBB/includes/ucp/ucp_pm_viewfolder.php
new file mode 100644
index 0000000000..33d2c9fb6f
--- /dev/null
+++ b/phpBB/includes/ucp/ucp_pm_viewfolder.php
@@ -0,0 +1,509 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* View message folder
+* Called from ucp_pm with mode == 'view' && action == 'view_folder'
+*/
+function view_folder($id, $mode, $folder_id, $folder)
+{
+ global $user, $template, $auth, $db, $cache;
+ global $phpbb_root_path, $config, $phpEx;
+
+ $submit_export = (isset($_POST['submit_export'])) ? true : false;
+
+ $folder_info = get_pm_from($folder_id, $folder, $user->data['user_id']);
+
+ if (!$submit_export)
+ {
+ $user->add_lang('viewforum');
+
+ // Grab icons
+ $icons = $cache->obtain_icons();
+
+ $color_rows = array('marked', 'replied');
+
+ // only show the friend/foe color rows if the module is enabled
+ $zebra_enabled = false;
+
+ $_module = new p_master();
+ $_module->list_modules('ucp');
+ $_module->set_active('zebra');
+
+ $zebra_enabled = ($_module->active_module === false) ? false : true;
+
+ unset($_module);
+
+ if ($zebra_enabled)
+ {
+ $color_rows = array_merge($color_rows, array('friend', 'foe'));
+ }
+
+ foreach ($color_rows as $var)
+ {
+ $template->assign_block_vars('pm_colour_info', array(
+ 'IMG' => $user->img("pm_{$var}", ''),
+ 'CLASS' => "pm_{$var}_colour",
+ 'LANG' => $user->lang[strtoupper($var) . '_MESSAGE'])
+ );
+ }
+
+ $mark_options = array('mark_important', 'delete_marked');
+
+ $s_mark_options = '';
+ foreach ($mark_options as $mark_option)
+ {
+ $s_mark_options .= '<option value="' . $mark_option . '">' . $user->lang[strtoupper($mark_option)] . '</option>';
+ }
+
+ // We do the folder moving options here too, for template authors to use...
+ $s_folder_move_options = '';
+ if ($folder_id != PRIVMSGS_NO_BOX && $folder_id != PRIVMSGS_OUTBOX)
+ {
+ foreach ($folder as $f_id => $folder_ary)
+ {
+ if ($f_id == PRIVMSGS_OUTBOX || $f_id == PRIVMSGS_SENTBOX || $f_id == $folder_id)
+ {
+ continue;
+ }
+
+ $s_folder_move_options .= '<option' . (($f_id != PRIVMSGS_INBOX) ? ' class="sep"' : '') . ' value="' . $f_id . '">';
+ $s_folder_move_options .= sprintf($user->lang['MOVE_MARKED_TO_FOLDER'], $folder_ary['folder_name']);
+ $s_folder_move_options .= (($folder_ary['unread_messages']) ? ' [' . $folder_ary['unread_messages'] . '] ' : '') . '</option>';
+ }
+ }
+ $friend = $foe = array();
+
+ // Get friends and foes
+ $sql = 'SELECT *
+ FROM ' . ZEBRA_TABLE . '
+ WHERE user_id = ' . $user->data['user_id'];
+ $result = $db->sql_query($sql);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $friend[$row['zebra_id']] = $row['friend'];
+ $foe[$row['zebra_id']] = $row['foe'];
+ }
+ $db->sql_freeresult($result);
+
+ $template->assign_vars(array(
+ 'S_MARK_OPTIONS' => $s_mark_options,
+ 'S_MOVE_MARKED_OPTIONS' => $s_folder_move_options)
+ );
+
+ // Okay, lets dump out the page ...
+ if (sizeof($folder_info['pm_list']))
+ {
+ $address_list = array();
+
+ // Build Recipient List if in outbox/sentbox - max two additional queries
+ if ($folder_id == PRIVMSGS_OUTBOX || $folder_id == PRIVMSGS_SENTBOX)
+ {
+ $address_list = get_recipient_strings($folder_info['rowset']);
+ }
+
+ foreach ($folder_info['pm_list'] as $message_id)
+ {
+ $row = &$folder_info['rowset'][$message_id];
+
+ $folder_img = ($row['pm_unread']) ? 'pm_unread' : 'pm_read';
+ $folder_alt = ($row['pm_unread']) ? 'NEW_MESSAGES' : 'NO_NEW_MESSAGES';
+
+ // Generate all URIs ...
+ $view_message_url = append_sid("{$phpbb_root_path}ucp.$phpEx", "i=$id&amp;mode=view&amp;f=$folder_id&amp;p=$message_id");
+ $remove_message_url = append_sid("{$phpbb_root_path}ucp.$phpEx", "i=$id&amp;mode=compose&amp;action=delete&amp;p=$message_id");
+
+ $row_indicator = '';
+ foreach ($color_rows as $var)
+ {
+ if (($var != 'friend' && $var != 'foe' && $row['pm_' . $var])
+ ||
+ (($var == 'friend' || $var == 'foe') && isset(${$var}[$row['author_id']]) && ${$var}[$row['author_id']]))
+ {
+ $row_indicator = $var;
+ break;
+ }
+ }
+
+ // Send vars to template
+ $template->assign_block_vars('messagerow', array(
+ 'PM_CLASS' => ($row_indicator) ? 'pm_' . $row_indicator . '_colour' : '',
+
+ 'MESSAGE_AUTHOR_FULL' => get_username_string('full', $row['author_id'], $row['username'], $row['user_colour'], $row['username']),
+ 'MESSAGE_AUTHOR_COLOUR' => get_username_string('colour', $row['author_id'], $row['username'], $row['user_colour'], $row['username']),
+ 'MESSAGE_AUTHOR' => get_username_string('username', $row['author_id'], $row['username'], $row['user_colour'], $row['username']),
+ 'U_MESSAGE_AUTHOR' => get_username_string('profile', $row['author_id'], $row['username'], $row['user_colour'], $row['username']),
+
+ 'FOLDER_ID' => $folder_id,
+ 'MESSAGE_ID' => $message_id,
+ 'SENT_TIME' => $user->format_date($row['message_time']),
+ 'SUBJECT' => censor_text($row['message_subject']),
+ 'FOLDER' => (isset($folder[$row['folder_id']])) ? $folder[$row['folder_id']]['folder_name'] : '',
+ 'U_FOLDER' => (isset($folder[$row['folder_id']])) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'folder=' . $row['folder_id']) : '',
+ 'PM_ICON_IMG' => (!empty($icons[$row['icon_id']])) ? '<img src="' . $config['icons_path'] . '/' . $icons[$row['icon_id']]['img'] . '" width="' . $icons[$row['icon_id']]['width'] . '" height="' . $icons[$row['icon_id']]['height'] . '" alt="" title="" />' : '',
+ 'PM_ICON_URL' => (!empty($icons[$row['icon_id']])) ? $config['icons_path'] . '/' . $icons[$row['icon_id']]['img'] : '',
+ 'FOLDER_IMG' => $user->img($folder_img, $folder_alt),
+ 'FOLDER_IMG_SRC' => $user->img($folder_img, $folder_alt, false, '', 'src'),
+ 'PM_IMG' => ($row_indicator) ? $user->img('pm_' . $row_indicator, '') : '',
+ 'ATTACH_ICON_IMG' => ($auth->acl_get('u_pm_download') && $row['message_attachment'] && $config['allow_pm_attach']) ? $user->img('icon_topic_attach', $user->lang['TOTAL_ATTACHMENTS']) : '',
+
+ 'S_PM_DELETED' => ($row['pm_deleted']) ? true : false,
+ 'S_AUTHOR_DELETED' => ($row['author_id'] == ANONYMOUS) ? true : false,
+
+ 'U_VIEW_PM' => ($row['pm_deleted']) ? '' : $view_message_url,
+ 'U_REMOVE_PM' => ($row['pm_deleted']) ? $remove_message_url : '',
+ 'RECIPIENTS' => ($folder_id == PRIVMSGS_OUTBOX || $folder_id == PRIVMSGS_SENTBOX) ? implode(', ', $address_list[$message_id]) : '')
+ );
+ }
+ unset($folder_info['rowset']);
+
+ $template->assign_vars(array(
+ 'S_SHOW_RECIPIENTS' => ($folder_id == PRIVMSGS_OUTBOX || $folder_id == PRIVMSGS_SENTBOX) ? true : false,
+ 'S_SHOW_COLOUR_LEGEND' => true,
+
+ 'S_PM_ICONS' => ($config['enable_pm_icons']) ? true : false)
+ );
+ }
+ }
+ else
+ {
+ $export_type = request_var('export_option', '');
+ $enclosure = request_var('enclosure', '');
+ $delimiter = request_var('delimiter', '');
+
+ if ($export_type == 'CSV' && ($delimiter === '' || $enclosure === ''))
+ {
+ $template->assign_var('PROMPT', true);
+ }
+ else
+ {
+ // Build Recipient List if in outbox/sentbox
+ $address = $data = array();
+
+ if ($folder_id == PRIVMSGS_OUTBOX || $folder_id == PRIVMSGS_SENTBOX)
+ {
+ foreach ($folder_info['rowset'] as $message_id => $row)
+ {
+ $address[$message_id] = rebuild_header(array('to' => $row['to_address'], 'bcc' => $row['bcc_address']));
+ }
+ }
+
+ foreach ($folder_info['pm_list'] as $message_id)
+ {
+ $row = &$folder_info['rowset'][$message_id];
+
+ include_once($phpbb_root_path . 'includes/functions_posting.' . $phpEx);
+
+ $sql = 'SELECT p.message_text, p.bbcode_uid
+ FROM ' . PRIVMSGS_TO_TABLE . ' t, ' . PRIVMSGS_TABLE . ' p, ' . USERS_TABLE . ' u
+ WHERE t.user_id = ' . $user->data['user_id'] . "
+ AND p.author_id = u.user_id
+ AND t.folder_id = $folder_id
+ AND t.msg_id = p.msg_id
+ AND p.msg_id = $message_id";
+ $result = $db->sql_query_limit($sql, 1);
+ $message_row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ $_types = array('u', 'g');
+ foreach ($_types as $ug_type)
+ {
+ if (isset($address[$message_id][$ug_type]) && sizeof($address[$message_id][$ug_type]))
+ {
+ if ($ug_type == 'u')
+ {
+ $sql = 'SELECT user_id as id, username as name
+ FROM ' . USERS_TABLE . '
+ WHERE ';
+ }
+ else
+ {
+ $sql = 'SELECT group_id as id, group_name as name
+ FROM ' . GROUPS_TABLE . '
+ WHERE ';
+ }
+ $sql .= $db->sql_in_set(($ug_type == 'u') ? 'user_id' : 'group_id', array_map('intval', array_keys($address[$message_id][$ug_type])));
+
+ $result = $db->sql_query($sql);
+
+ while ($info_row = $db->sql_fetchrow($result))
+ {
+ $address[$message_id][$ug_type][$address[$message_id][$ug_type][$info_row['id']]][] = $info_row['name'];
+ unset($address[$message_id][$ug_type][$info_row['id']]);
+ }
+ $db->sql_freeresult($result);
+ }
+ }
+
+ decode_message($message_row['message_text'], $message_row['bbcode_uid']);
+
+ $data[] = array(
+ 'subject' => censor_text($row['message_subject']),
+ 'sender' => $row['username'],
+ // ISO 8601 date. For PHP4 we are able to hardcode the timezone because $user->format_date() does not set it.
+ 'date' => $user->format_date($row['message_time'], (PHP_VERSION >= 5) ? 'c' : "Y-m-d\TH:i:s+00:00", true),
+ 'to' => ($folder_id == PRIVMSGS_OUTBOX || $folder_id == PRIVMSGS_SENTBOX) ? $address[$message_id] : '',
+ 'message' => $message_row['message_text']
+ );
+ }
+
+ switch ($export_type)
+ {
+ case 'CSV':
+ case 'CSV_EXCEL':
+ $mimetype = 'text/csv';
+ $filetype = 'csv';
+
+ if ($export_type == 'CSV_EXCEL')
+ {
+ $enclosure = '"';
+ $delimiter = ',';
+ $newline = "\r\n";
+ }
+ else
+ {
+ $newline = "\n";
+ }
+
+ $string = '';
+ foreach ($data as $value)
+ {
+ $recipients = $value['to'];
+ $value['to'] = $value['bcc'] = '';
+
+ if (is_array($recipients))
+ {
+ foreach ($recipients as $values)
+ {
+ $value['bcc'] .= (isset($values['bcc']) && is_array($values['bcc'])) ? ',' . implode(',', $values['bcc']) : '';
+ $value['to'] .= (isset($values['to']) && is_array($values['to'])) ? ',' . implode(',', $values['to']) : '';
+ }
+
+ // Remove the commas which will appear before the first entry.
+ $value['to'] = substr($value['to'], 1);
+ $value['bcc'] = substr($value['bcc'], 1);
+ }
+
+ foreach ($value as $tag => $text)
+ {
+ $cell = str_replace($enclosure, $enclosure . $enclosure, $text);
+
+ if (strpos($cell, $enclosure) !== false || strpos($cell, $delimiter) !== false || strpos($cell, $newline) !== false)
+ {
+ $string .= $enclosure . $text . $enclosure . $delimiter;
+ }
+ else
+ {
+ $string .= $cell . $delimiter;
+ }
+ }
+ $string = substr($string, 0, -1) . $newline;
+ }
+ break;
+
+ case 'XML':
+ $mimetype = 'application/xml';
+ $filetype = 'xml';
+ $string = '<?xml version="1.0"?>' . "\n";
+ $string .= "<phpbb>\n";
+
+ foreach ($data as $value)
+ {
+ $string .= "\t<privmsg>\n";
+
+ if (is_array($value['to']))
+ {
+ foreach ($value['to'] as $key => $values)
+ {
+ foreach ($values as $type => $types)
+ {
+ foreach ($types as $name)
+ {
+ $string .= "\t\t<recipient type=\"$type\" status=\"$key\">$name</recipient>\n";
+ }
+ }
+ }
+ }
+
+ unset($value['to']);
+
+ foreach ($value as $tag => $text)
+ {
+ $string .= "\t\t<$tag>$text</$tag>\n";
+ }
+
+ $string .= "\t</privmsg>\n";
+ }
+ $string .= '</phpbb>';
+ break;
+ }
+
+ header('Pragma: no-cache');
+ header("Content-Type: $mimetype; name=\"data.$filetype\"");
+ header("Content-disposition: attachment; filename=data.$filetype");
+ echo $string;
+ exit;
+ }
+ }
+}
+
+/**
+* Get Messages from folder/user
+*/
+function get_pm_from($folder_id, $folder, $user_id)
+{
+ global $user, $db, $template, $config, $auth, $phpbb_root_path, $phpEx;
+
+ $start = request_var('start', 0);
+
+ // Additional vars later, pm ordering is mostly different from post ordering. :/
+ $sort_days = request_var('st', 0);
+ $sort_key = request_var('sk', 't');
+ $sort_dir = request_var('sd', 'd');
+
+ // PM ordering options
+ $limit_days = array(0 => $user->lang['ALL_MESSAGES'], 1 => $user->lang['1_DAY'], 7 => $user->lang['7_DAYS'], 14 => $user->lang['2_WEEKS'], 30 => $user->lang['1_MONTH'], 90 => $user->lang['3_MONTHS'], 180 => $user->lang['6_MONTHS'], 365 => $user->lang['1_YEAR']);
+
+ // No sort by Author for sentbox/outbox (already only author available)
+ // Also, sort by msg_id for the time - private messages are not as prone to errors as posts are.
+ if ($folder_id == PRIVMSGS_OUTBOX || $folder_id == PRIVMSGS_SENTBOX)
+ {
+ $sort_by_text = array('t' => $user->lang['POST_TIME'], 's' => $user->lang['SUBJECT']);
+ $sort_by_sql = array('t' => 'p.message_time', 's' => array('p.message_subject', 'p.message_time'));
+ }
+ else
+ {
+ $sort_by_text = array('a' => $user->lang['AUTHOR'], 't' => $user->lang['POST_TIME'], 's' => $user->lang['SUBJECT']);
+ $sort_by_sql = array('a' => array('u.username_clean', 'p.message_time'), 't' => 'p.message_time', 's' => array('p.message_subject', 'p.message_time'));
+ }
+
+ $s_limit_days = $s_sort_key = $s_sort_dir = $u_sort_param = '';
+ gen_sort_selects($limit_days, $sort_by_text, $sort_days, $sort_key, $sort_dir, $s_limit_days, $s_sort_key, $s_sort_dir, $u_sort_param);
+
+ $folder_sql = 't.folder_id = ' . (int) $folder_id;
+
+ // Limit pms to certain time frame, obtain correct pm count
+ if ($sort_days)
+ {
+ $min_post_time = time() - ($sort_days * 86400);
+
+ if (isset($_POST['sort']))
+ {
+ $start = 0;
+ }
+
+ $sql = 'SELECT COUNT(t.msg_id) AS pm_count
+ FROM ' . PRIVMSGS_TO_TABLE . ' t, ' . PRIVMSGS_TABLE . " p
+ WHERE $folder_sql
+ AND t.user_id = $user_id
+ AND t.msg_id = p.msg_id
+ AND p.message_time >= $min_post_time";
+ $result = $db->sql_query_limit($sql, 1);
+ $pm_count = (int) $db->sql_fetchfield('pm_count');
+ $db->sql_freeresult($result);
+
+ $sql_limit_time = "AND p.message_time >= $min_post_time";
+ }
+ else
+ {
+ $pm_count = (!empty($folder[$folder_id]['num_messages'])) ? $folder[$folder_id]['num_messages'] : 0;
+ $sql_limit_time = '';
+ }
+
+ $template->assign_vars(array(
+ 'PAGINATION' => generate_pagination(append_sid("{$phpbb_root_path}ucp.$phpEx", "i=pm&amp;mode=view&amp;action=view_folder&amp;f=$folder_id&amp;$u_sort_param"), $pm_count, $config['topics_per_page'], $start),
+ 'PAGE_NUMBER' => on_page($pm_count, $config['topics_per_page'], $start),
+ 'TOTAL_MESSAGES' => (($pm_count == 1) ? $user->lang['VIEW_PM_MESSAGE'] : sprintf($user->lang['VIEW_PM_MESSAGES'], $pm_count)),
+
+ 'POST_IMG' => (!$auth->acl_get('u_sendpm')) ? $user->img('button_topic_locked', 'POST_PM_LOCKED') : $user->img('button_pm_new', 'POST_NEW_PM'),
+
+ 'S_NO_AUTH_SEND_MESSAGE' => !$auth->acl_get('u_sendpm'),
+
+ 'S_SELECT_SORT_DIR' => $s_sort_dir,
+ 'S_SELECT_SORT_KEY' => $s_sort_key,
+ 'S_SELECT_SORT_DAYS' => $s_limit_days,
+ 'S_TOPIC_ICONS' => ($config['enable_pm_icons']) ? true : false,
+
+ 'U_POST_NEW_TOPIC' => ($auth->acl_get('u_sendpm')) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;mode=compose') : '',
+ 'S_PM_ACTION' => append_sid("{$phpbb_root_path}ucp.$phpEx", "i=pm&amp;mode=view&amp;action=view_folder&amp;f=$folder_id" . (($start !== 0) ? "&amp;start=$start" : '')),
+ ));
+
+ // Grab all pm data
+ $rowset = $pm_list = array();
+
+ // If the user is trying to reach late pages, start searching from the end
+ $store_reverse = false;
+ $sql_limit = $config['topics_per_page'];
+ if ($start > $pm_count / 2)
+ {
+ $store_reverse = true;
+
+ if ($start + $config['topics_per_page'] > $pm_count)
+ {
+ $sql_limit = min($config['topics_per_page'], max(1, $pm_count - $start));
+ }
+
+ // Select the sort order
+ $direction = ($sort_dir == 'd') ? 'ASC' : 'DESC';
+ $sql_start = max(0, $pm_count - $sql_limit - $start);
+ }
+ else
+ {
+ // Select the sort order
+ $direction = ($sort_dir == 'd') ? 'DESC' : 'ASC';
+ $sql_start = $start;
+ }
+
+ // Sql sort order
+ if (is_array($sort_by_sql[$sort_key]))
+ {
+ $sql_sort_order = implode(' ' . $direction . ', ', $sort_by_sql[$sort_key]) . ' ' . $direction;
+ }
+ else
+ {
+ $sql_sort_order = $sort_by_sql[$sort_key] . ' ' . $direction;
+ }
+
+ $sql = 'SELECT t.*, p.root_level, p.message_time, p.message_subject, p.icon_id, p.to_address, p.message_attachment, p.bcc_address, u.username, u.username_clean, u.user_colour
+ FROM ' . PRIVMSGS_TO_TABLE . ' t, ' . PRIVMSGS_TABLE . ' p, ' . USERS_TABLE . " u
+ WHERE t.user_id = $user_id
+ AND p.author_id = u.user_id
+ AND $folder_sql
+ AND t.msg_id = p.msg_id
+ $sql_limit_time
+ ORDER BY $sql_sort_order";
+ $result = $db->sql_query_limit($sql, $sql_limit, $sql_start);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $rowset[$row['msg_id']] = $row;
+ $pm_list[] = $row['msg_id'];
+ }
+ $db->sql_freeresult($result);
+
+ $pm_list = ($store_reverse) ? array_reverse($pm_list) : $pm_list;
+
+ return array(
+ 'pm_count' => $pm_count,
+ 'pm_list' => $pm_list,
+ 'rowset' => $rowset
+ );
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/ucp_pm_viewmessage.php b/phpBB/includes/ucp/ucp_pm_viewmessage.php
new file mode 100644
index 0000000000..26968e1382
--- /dev/null
+++ b/phpBB/includes/ucp/ucp_pm_viewmessage.php
@@ -0,0 +1,320 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* View private message
+*/
+function view_message($id, $mode, $folder_id, $msg_id, $folder, $message_row)
+{
+ global $user, $template, $auth, $db, $cache;
+ global $phpbb_root_path, $phpEx, $config;
+
+ $user->add_lang(array('viewtopic', 'memberlist'));
+
+ $msg_id = (int) $msg_id;
+ $folder_id = (int) $folder_id;
+ $author_id = (int) $message_row['author_id'];
+ $view = request_var('view', '');
+
+ // Not able to view message, it was deleted by the sender
+ if ($message_row['pm_deleted'])
+ {
+ $meta_info = append_sid("{$phpbb_root_path}ucp.$phpEx", "i=pm&amp;folder=$folder_id");
+ $message = $user->lang['NO_AUTH_READ_REMOVED_MESSAGE'];
+
+ $message .= '<br /><br />' . sprintf($user->lang['RETURN_FOLDER'], '<a href="' . $meta_info . '">', '</a>');
+ trigger_error($message);
+ }
+
+ // Do not allow hold messages to be seen
+ if ($folder_id == PRIVMSGS_HOLD_BOX)
+ {
+ trigger_error('NO_AUTH_READ_HOLD_MESSAGE');
+ }
+
+ // Grab icons
+ $icons = $cache->obtain_icons();
+
+ $bbcode = false;
+
+ // Instantiate BBCode if need be
+ if ($message_row['bbcode_bitfield'])
+ {
+ include($phpbb_root_path . 'includes/bbcode.' . $phpEx);
+ $bbcode = new bbcode($message_row['bbcode_bitfield']);
+ }
+
+ // Assign TO/BCC Addresses to template
+ write_pm_addresses(array('to' => $message_row['to_address'], 'bcc' => $message_row['bcc_address']), $author_id);
+
+ $user_info = get_user_information($author_id, $message_row);
+
+ // Parse the message and subject
+ $message = censor_text($message_row['message_text']);
+
+ // Second parse bbcode here
+ if ($message_row['bbcode_bitfield'])
+ {
+ $bbcode->bbcode_second_pass($message, $message_row['bbcode_uid'], $message_row['bbcode_bitfield']);
+ }
+
+ // Always process smilies after parsing bbcodes
+ $message = bbcode_nl2br($message);
+ $message = smiley_text($message);
+
+ // Replace naughty words such as farty pants
+ $message_row['message_subject'] = censor_text($message_row['message_subject']);
+
+ // Editing information
+ if ($message_row['message_edit_count'] && $config['display_last_edited'])
+ {
+ $l_edit_time_total = ($message_row['message_edit_count'] == 1) ? $user->lang['EDITED_TIME_TOTAL'] : $user->lang['EDITED_TIMES_TOTAL'];
+ $l_edited_by = '<br /><br />' . sprintf($l_edit_time_total, (!$message_row['message_edit_user']) ? $message_row['username'] : $message_row['message_edit_user'], $user->format_date($message_row['message_edit_time'], false, true), $message_row['message_edit_count']);
+ }
+ else
+ {
+ $l_edited_by = '';
+ }
+
+ // Pull attachment data
+ $display_notice = false;
+ $attachments = array();
+
+ if ($message_row['message_attachment'] && $config['allow_pm_attach'])
+ {
+ if ($auth->acl_get('u_pm_download'))
+ {
+ $sql = 'SELECT *
+ FROM ' . ATTACHMENTS_TABLE . "
+ WHERE post_msg_id = $msg_id
+ AND in_message = 1
+ ORDER BY filetime DESC, post_msg_id ASC";
+ $result = $db->sql_query($sql);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $attachments[] = $row;
+ }
+ $db->sql_freeresult($result);
+
+ // No attachments exist, but message table thinks they do so go ahead and reset attach flags
+ if (!sizeof($attachments))
+ {
+ $sql = 'UPDATE ' . PRIVMSGS_TABLE . "
+ SET message_attachment = 0
+ WHERE msg_id = $msg_id";
+ $db->sql_query($sql);
+ }
+ }
+ else
+ {
+ $display_notice = true;
+ }
+ }
+
+ // Assign inline attachments
+ if (!empty($attachments))
+ {
+ $update_count = array();
+ parse_attachments(false, $message, $attachments, $update_count);
+
+ // Update the attachment download counts
+ if (sizeof($update_count))
+ {
+ $sql = 'UPDATE ' . ATTACHMENTS_TABLE . '
+ SET download_count = download_count + 1
+ WHERE ' . $db->sql_in_set('attach_id', array_unique($update_count));
+ $db->sql_query($sql);
+ }
+ }
+
+ $user_info['sig'] = '';
+
+ $signature = ($message_row['enable_sig'] && $config['allow_sig'] && $auth->acl_get('u_sig') && $user->optionget('viewsigs')) ? $user_info['user_sig'] : '';
+
+ // End signature parsing, only if needed
+ if ($signature)
+ {
+ $signature = censor_text($signature);
+
+ if ($user_info['user_sig_bbcode_bitfield'])
+ {
+ if ($bbcode === false)
+ {
+ include($phpbb_root_path . 'includes/bbcode.' . $phpEx);
+ $bbcode = new bbcode($user_info['user_sig_bbcode_bitfield']);
+ }
+
+ $bbcode->bbcode_second_pass($signature, $user_info['user_sig_bbcode_uid'], $user_info['user_sig_bbcode_bitfield']);
+ }
+
+ $signature = bbcode_nl2br($signature);
+ $signature = smiley_text($signature);
+ }
+
+ $url = append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm');
+
+ // Number of "to" recipients
+ $num_recipients = (int) preg_match_all('/:?(u|g)_([0-9]+):?/', $message_row['to_address'], $match);
+
+ $template->assign_vars(array(
+ 'MESSAGE_AUTHOR_FULL' => get_username_string('full', $author_id, $user_info['username'], $user_info['user_colour'], $user_info['username']),
+ 'MESSAGE_AUTHOR_COLOUR' => get_username_string('colour', $author_id, $user_info['username'], $user_info['user_colour'], $user_info['username']),
+ 'MESSAGE_AUTHOR' => get_username_string('username', $author_id, $user_info['username'], $user_info['user_colour'], $user_info['username']),
+ 'U_MESSAGE_AUTHOR' => get_username_string('profile', $author_id, $user_info['username'], $user_info['user_colour'], $user_info['username']),
+
+ 'RANK_TITLE' => $user_info['rank_title'],
+ 'RANK_IMG' => $user_info['rank_image'],
+ 'AUTHOR_AVATAR' => (isset($user_info['avatar'])) ? $user_info['avatar'] : '',
+ 'AUTHOR_JOINED' => $user->format_date($user_info['user_regdate']),
+ 'AUTHOR_POSTS' => (int) $user_info['user_posts'],
+ 'AUTHOR_FROM' => (!empty($user_info['user_from'])) ? $user_info['user_from'] : '',
+
+ 'ONLINE_IMG' => (!$config['load_onlinetrack']) ? '' : ((isset($user_info['online']) && $user_info['online']) ? $user->img('icon_user_online', $user->lang['ONLINE']) : $user->img('icon_user_offline', $user->lang['OFFLINE'])),
+ 'S_ONLINE' => (!$config['load_onlinetrack']) ? false : ((isset($user_info['online']) && $user_info['online']) ? true : false),
+ 'DELETE_IMG' => $user->img('icon_post_delete', $user->lang['DELETE_MESSAGE']),
+ 'INFO_IMG' => $user->img('icon_post_info', $user->lang['VIEW_PM_INFO']),
+ 'PROFILE_IMG' => $user->img('icon_user_profile', $user->lang['READ_PROFILE']),
+ 'EMAIL_IMG' => $user->img('icon_contact_email', $user->lang['SEND_EMAIL']),
+ 'QUOTE_IMG' => $user->img('icon_post_quote', $user->lang['POST_QUOTE_PM']),
+ 'REPLY_IMG' => $user->img('button_pm_reply', $user->lang['POST_REPLY_PM']),
+ 'REPORT_IMG' => $user->img('icon_post_report', 'REPORT_PM'),
+ 'EDIT_IMG' => $user->img('icon_post_edit', $user->lang['POST_EDIT_PM']),
+ 'MINI_POST_IMG' => $user->img('icon_post_target', $user->lang['PM']),
+
+ 'SENT_DATE' => ($view == 'print') ? $user->format_date($message_row['message_time'], false, true) : $user->format_date($message_row['message_time']),
+ 'SUBJECT' => $message_row['message_subject'],
+ 'MESSAGE' => $message,
+ 'SIGNATURE' => ($message_row['enable_sig']) ? $signature : '',
+ 'EDITED_MESSAGE' => $l_edited_by,
+ 'MESSAGE_ID' => $message_row['msg_id'],
+
+ 'U_PM' => ($config['allow_privmsg'] && $auth->acl_get('u_sendpm') && ($user_info['user_allow_pm'] || $auth->acl_gets('a_', 'm_') || $auth->acl_getf_global('m_'))) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;mode=compose&amp;u=' . $author_id) : '',
+ 'U_WWW' => (!empty($user_info['user_website'])) ? $user_info['user_website'] : '',
+ 'U_ICQ' => ($user_info['user_icq']) ? 'http://www.icq.com/people/webmsg.php?to=' . urlencode($user_info['user_icq']) : '',
+ 'U_AIM' => ($user_info['user_aim'] && $auth->acl_get('u_sendim')) ? append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=contact&amp;action=aim&amp;u=' . $author_id) : '',
+ 'U_YIM' => ($user_info['user_yim']) ? 'http://edit.yahoo.com/config/send_webmesg?.target=' . urlencode($user_info['user_yim']) . '&amp;.src=pg' : '',
+ 'U_MSN' => ($user_info['user_msnm'] && $auth->acl_get('u_sendim')) ? append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=contact&amp;action=msnm&amp;u=' . $author_id) : '',
+ 'U_JABBER' => ($user_info['user_jabber'] && $auth->acl_get('u_sendim')) ? append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=contact&amp;action=jabber&amp;u=' . $author_id) : '',
+
+ 'U_DELETE' => ($auth->acl_get('u_pm_delete')) ? "$url&amp;mode=compose&amp;action=delete&amp;f=$folder_id&amp;p=" . $message_row['msg_id'] : '',
+ 'U_EMAIL' => $user_info['email'],
+ 'U_REPORT' => ($config['allow_pm_report']) ? append_sid("{$phpbb_root_path}report.$phpEx", "pm=" . $message_row['msg_id']) : '',
+ 'U_QUOTE' => ($auth->acl_get('u_sendpm') && $author_id != ANONYMOUS) ? "$url&amp;mode=compose&amp;action=quote&amp;f=$folder_id&amp;p=" . $message_row['msg_id'] : '',
+ 'U_EDIT' => (($message_row['message_time'] > time() - ($config['pm_edit_time'] * 60) || !$config['pm_edit_time']) && $folder_id == PRIVMSGS_OUTBOX && $auth->acl_get('u_pm_edit')) ? "$url&amp;mode=compose&amp;action=edit&amp;f=$folder_id&amp;p=" . $message_row['msg_id'] : '',
+ 'U_POST_REPLY_PM' => ($auth->acl_get('u_sendpm') && $author_id != ANONYMOUS) ? "$url&amp;mode=compose&amp;action=reply&amp;f=$folder_id&amp;p=" . $message_row['msg_id'] : '',
+ 'U_POST_REPLY_ALL' => ($auth->acl_get('u_sendpm') && $author_id != ANONYMOUS) ? "$url&amp;mode=compose&amp;action=reply&amp;f=$folder_id&amp;reply_to_all=1&amp;p=" . $message_row['msg_id'] : '',
+ 'U_PREVIOUS_PM' => "$url&amp;f=$folder_id&amp;p=" . $message_row['msg_id'] . "&amp;view=previous",
+ 'U_NEXT_PM' => "$url&amp;f=$folder_id&amp;p=" . $message_row['msg_id'] . "&amp;view=next",
+
+ 'U_PM_ACTION' => $url . '&amp;mode=compose&amp;f=' . $folder_id . '&amp;p=' . $message_row['msg_id'],
+
+ 'S_HAS_ATTACHMENTS' => (sizeof($attachments)) ? true : false,
+ 'S_DISPLAY_NOTICE' => $display_notice && $message_row['message_attachment'],
+ 'S_AUTHOR_DELETED' => ($author_id == ANONYMOUS) ? true : false,
+ 'S_SPECIAL_FOLDER' => in_array($folder_id, array(PRIVMSGS_NO_BOX, PRIVMSGS_OUTBOX)),
+ 'S_PM_RECIPIENTS' => $num_recipients,
+
+ 'U_PRINT_PM' => ($config['print_pm'] && $auth->acl_get('u_pm_printpm')) ? "$url&amp;f=$folder_id&amp;p=" . $message_row['msg_id'] . "&amp;view=print" : '',
+ 'U_FORWARD_PM' => ($config['forward_pm'] && $auth->acl_get('u_sendpm') && $auth->acl_get('u_pm_forward')) ? "$url&amp;mode=compose&amp;action=forward&amp;f=$folder_id&amp;p=" . $message_row['msg_id'] : '')
+ );
+
+ // Display not already displayed Attachments for this post, we already parsed them. ;)
+ if (isset($attachments) && sizeof($attachments))
+ {
+ foreach ($attachments as $attachment)
+ {
+ $template->assign_block_vars('attachment', array(
+ 'DISPLAY_ATTACHMENT' => $attachment)
+ );
+ }
+ }
+
+ if (!isset($_REQUEST['view']) || $_REQUEST['view'] != 'print')
+ {
+ // Message History
+ if (message_history($msg_id, $user->data['user_id'], $message_row, $folder))
+ {
+ $template->assign_var('S_DISPLAY_HISTORY', true);
+ }
+ }
+}
+
+/**
+* Get user information (only for message display)
+*/
+function get_user_information($user_id, $user_row)
+{
+ global $db, $auth, $user, $cache;
+ global $phpbb_root_path, $phpEx, $config;
+
+ if (!$user_id)
+ {
+ return array();
+ }
+
+ if (empty($user_row))
+ {
+ $sql = 'SELECT *
+ FROM ' . USERS_TABLE . '
+ WHERE user_id = ' . (int) $user_id;
+ $result = $db->sql_query($sql);
+ $user_row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+ }
+
+ // Some standard values
+ $user_row['online'] = false;
+ $user_row['rank_title'] = $user_row['rank_image'] = $user_row['rank_image_src'] = $user_row['email'] = '';
+
+ // Generate online information for user
+ if ($config['load_onlinetrack'])
+ {
+ $sql = 'SELECT session_user_id, MAX(session_time) as online_time, MIN(session_viewonline) AS viewonline
+ FROM ' . SESSIONS_TABLE . "
+ WHERE session_user_id = $user_id
+ GROUP BY session_user_id";
+ $result = $db->sql_query_limit($sql, 1);
+ $row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ $update_time = $config['load_online_time'] * 60;
+ if ($row)
+ {
+ $user_row['online'] = (time() - $update_time < $row['online_time'] && ($row['viewonline'] || $auth->acl_get('u_viewonline'))) ? true : false;
+ }
+ }
+
+ if (!function_exists('get_user_avatar'))
+ {
+ include($phpbb_root_path . 'includes/functions_display.' . $phpEx);
+ }
+
+ $user_row['avatar'] = ($user->optionget('viewavatars')) ? get_user_avatar($user_row['user_avatar'], $user_row['user_avatar_type'], $user_row['user_avatar_width'], $user_row['user_avatar_height']) : '';
+
+ get_user_rank($user_row['user_rank'], $user_row['user_posts'], $user_row['rank_title'], $user_row['rank_image'], $user_row['rank_image_src']);
+
+ if (!empty($user_row['user_allow_viewemail']) || $auth->acl_get('a_email'))
+ {
+ $user_row['email'] = ($config['board_email_form'] && $config['email_enable']) ? append_sid("{$phpbb_root_path}memberlist.$phpEx", "mode=email&amp;u=$user_id") : ((($config['board_hide_emails'] && !$auth->acl_get('a_email')) || empty($user_row['user_email'])) ? '' : 'mailto:' . $user_row['user_email']);
+ }
+
+ return $user_row;
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/ucp_prefs.php b/phpBB/includes/ucp/ucp_prefs.php
new file mode 100644
index 0000000000..cc8565e69d
--- /dev/null
+++ b/phpBB/includes/ucp/ucp_prefs.php
@@ -0,0 +1,357 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* ucp_prefs
+* Changing user preferences
+* @package ucp
+*/
+class ucp_prefs
+{
+ var $u_action;
+
+ function main($id, $mode)
+ {
+ global $config, $db, $user, $auth, $template, $phpbb_root_path, $phpEx;
+
+ $submit = (isset($_POST['submit'])) ? true : false;
+ $error = $data = array();
+ $s_hidden_fields = '';
+
+ switch ($mode)
+ {
+ case 'personal':
+ add_form_key('ucp_prefs_personal');
+ $data = array(
+ 'notifymethod' => request_var('notifymethod', $user->data['user_notify_type']),
+ 'dateformat' => request_var('dateformat', $user->data['user_dateformat'], true),
+ 'lang' => basename(request_var('lang', $user->data['user_lang'])),
+ 'style' => request_var('style', (int) $user->data['user_style']),
+ 'tz' => request_var('tz', (float) $user->data['user_timezone']),
+
+ 'dst' => request_var('dst', (bool) $user->data['user_dst']),
+ 'viewemail' => request_var('viewemail', (bool) $user->data['user_allow_viewemail']),
+ 'massemail' => request_var('massemail', (bool) $user->data['user_allow_massemail']),
+ 'hideonline' => request_var('hideonline', (bool) !$user->data['user_allow_viewonline']),
+ 'notifypm' => request_var('notifypm', (bool) $user->data['user_notify_pm']),
+ 'popuppm' => request_var('popuppm', (bool) $user->optionget('popuppm')),
+ 'allowpm' => request_var('allowpm', (bool) $user->data['user_allow_pm']),
+ );
+
+ if ($data['notifymethod'] == NOTIFY_IM && (!$config['jab_enable'] || !$user->data['user_jabber'] || !@extension_loaded('xml')))
+ {
+ // Jabber isnt enabled, or no jabber field filled in. Update the users table to be sure its correct.
+ $data['notifymethod'] = NOTIFY_BOTH;
+ }
+
+ if ($submit)
+ {
+ $data['style'] = ($config['override_user_style']) ? $config['default_style'] : $data['style'];
+
+ $error = validate_data($data, array(
+ 'dateformat' => array('string', false, 1, 30),
+ 'lang' => array('match', false, '#^[a-z0-9_\-]{2,}$#i'),
+ 'tz' => array('num', false, -14, 14),
+ ));
+
+ if (!check_form_key('ucp_prefs_personal'))
+ {
+ $error[] = 'FORM_INVALID';
+ }
+
+ if (!sizeof($error))
+ {
+ $user->optionset('popuppm', $data['popuppm']);
+
+ $sql_ary = array(
+ 'user_allow_pm' => $data['allowpm'],
+ 'user_allow_viewemail' => $data['viewemail'],
+ 'user_allow_massemail' => $data['massemail'],
+ 'user_allow_viewonline' => ($auth->acl_get('u_hideonline')) ? !$data['hideonline'] : $user->data['user_allow_viewonline'],
+ 'user_notify_type' => $data['notifymethod'],
+ 'user_notify_pm' => $data['notifypm'],
+ 'user_options' => $user->data['user_options'],
+
+ 'user_dst' => $data['dst'],
+ 'user_dateformat' => $data['dateformat'],
+ 'user_lang' => $data['lang'],
+ 'user_timezone' => $data['tz'],
+ 'user_style' => $data['style'],
+ );
+
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
+ WHERE user_id = ' . $user->data['user_id'];
+ $db->sql_query($sql);
+
+ meta_refresh(3, $this->u_action);
+ $message = $user->lang['PREFERENCES_UPDATED'] . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $this->u_action . '">', '</a>');
+ trigger_error($message);
+ }
+
+ // Replace "error" strings with their real, localised form
+ $error = preg_replace('#^([A-Z_]+)$#e', "(!empty(\$user->lang['\\1'])) ? \$user->lang['\\1'] : '\\1'", $error);
+ }
+
+ $dateformat_options = '';
+
+ foreach ($user->lang['dateformats'] as $format => $null)
+ {
+ $dateformat_options .= '<option value="' . $format . '"' . (($format == $data['dateformat']) ? ' selected="selected"' : '') . '>';
+ $dateformat_options .= $user->format_date(time(), $format, false) . ((strpos($format, '|') !== false) ? $user->lang['VARIANT_DATE_SEPARATOR'] . $user->format_date(time(), $format, true) : '');
+ $dateformat_options .= '</option>';
+ }
+
+ $s_custom = false;
+
+ $dateformat_options .= '<option value="custom"';
+ if (!isset($user->lang['dateformats'][$data['dateformat']]))
+ {
+ $dateformat_options .= ' selected="selected"';
+ $s_custom = true;
+ }
+ $dateformat_options .= '>' . $user->lang['CUSTOM_DATEFORMAT'] . '</option>';
+
+ $template->assign_vars(array(
+ 'ERROR' => (sizeof($error)) ? implode('<br />', $error) : '',
+
+ 'S_NOTIFY_EMAIL' => ($data['notifymethod'] == NOTIFY_EMAIL) ? true : false,
+ 'S_NOTIFY_IM' => ($data['notifymethod'] == NOTIFY_IM) ? true : false,
+ 'S_NOTIFY_BOTH' => ($data['notifymethod'] == NOTIFY_BOTH) ? true : false,
+ 'S_VIEW_EMAIL' => $data['viewemail'],
+ 'S_MASS_EMAIL' => $data['massemail'],
+ 'S_ALLOW_PM' => $data['allowpm'],
+ 'S_HIDE_ONLINE' => $data['hideonline'],
+ 'S_NOTIFY_PM' => $data['notifypm'],
+ 'S_POPUP_PM' => $data['popuppm'],
+ 'S_DST' => $data['dst'],
+
+ 'DATE_FORMAT' => $data['dateformat'],
+ 'A_DATE_FORMAT' => addslashes($data['dateformat']),
+ 'S_DATEFORMAT_OPTIONS' => $dateformat_options,
+ 'S_CUSTOM_DATEFORMAT' => $s_custom,
+ 'DEFAULT_DATEFORMAT' => $config['default_dateformat'],
+ 'A_DEFAULT_DATEFORMAT' => addslashes($config['default_dateformat']),
+
+ 'S_LANG_OPTIONS' => language_select($data['lang']),
+ 'S_STYLE_OPTIONS' => ($config['override_user_style']) ? '' : style_select($data['style']),
+ 'S_TZ_OPTIONS' => tz_select($data['tz'], true),
+ 'S_CAN_HIDE_ONLINE' => ($auth->acl_get('u_hideonline')) ? true : false,
+ 'S_SELECT_NOTIFY' => ($config['jab_enable'] && $user->data['user_jabber'] && @extension_loaded('xml')) ? true : false)
+ );
+
+ break;
+
+ case 'view':
+
+ add_form_key('ucp_prefs_view');
+
+ $data = array(
+ 'topic_sk' => request_var('topic_sk', (!empty($user->data['user_topic_sortby_type'])) ? $user->data['user_topic_sortby_type'] : 't'),
+ 'topic_sd' => request_var('topic_sd', (!empty($user->data['user_topic_sortby_dir'])) ? $user->data['user_topic_sortby_dir'] : 'd'),
+ 'topic_st' => request_var('topic_st', (!empty($user->data['user_topic_show_days'])) ? $user->data['user_topic_show_days'] : 0),
+
+ 'post_sk' => request_var('post_sk', (!empty($user->data['user_post_sortby_type'])) ? $user->data['user_post_sortby_type'] : 't'),
+ 'post_sd' => request_var('post_sd', (!empty($user->data['user_post_sortby_dir'])) ? $user->data['user_post_sortby_dir'] : 'a'),
+ 'post_st' => request_var('post_st', (!empty($user->data['user_post_show_days'])) ? $user->data['user_post_show_days'] : 0),
+
+ 'images' => request_var('images', (bool) $user->optionget('viewimg')),
+ 'flash' => request_var('flash', (bool) $user->optionget('viewflash')),
+ 'smilies' => request_var('smilies', (bool) $user->optionget('viewsmilies')),
+ 'sigs' => request_var('sigs', (bool) $user->optionget('viewsigs')),
+ 'avatars' => request_var('avatars', (bool) $user->optionget('viewavatars')),
+ 'wordcensor' => request_var('wordcensor', (bool) $user->optionget('viewcensors')),
+ );
+
+ if ($submit)
+ {
+ $error = validate_data($data, array(
+ 'topic_sk' => array('string', false, 1, 1),
+ 'topic_sd' => array('string', false, 1, 1),
+ 'post_sk' => array('string', false, 1, 1),
+ 'post_sd' => array('string', false, 1, 1),
+ ));
+
+ if (!check_form_key('ucp_prefs_view'))
+ {
+ $error[] = 'FORM_INVALID';
+ }
+
+ if (!sizeof($error))
+ {
+ $user->optionset('viewimg', $data['images']);
+ $user->optionset('viewflash', $data['flash']);
+ $user->optionset('viewsmilies', $data['smilies']);
+ $user->optionset('viewsigs', $data['sigs']);
+ $user->optionset('viewavatars', $data['avatars']);
+
+ if ($auth->acl_get('u_chgcensors'))
+ {
+ $user->optionset('viewcensors', $data['wordcensor']);
+ }
+
+ $sql_ary = array(
+ 'user_options' => $user->data['user_options'],
+ 'user_topic_sortby_type' => $data['topic_sk'],
+ 'user_post_sortby_type' => $data['post_sk'],
+ 'user_topic_sortby_dir' => $data['topic_sd'],
+ 'user_post_sortby_dir' => $data['post_sd'],
+
+ 'user_topic_show_days' => $data['topic_st'],
+ 'user_post_show_days' => $data['post_st'],
+ );
+
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
+ WHERE user_id = ' . $user->data['user_id'];
+ $db->sql_query($sql);
+
+ meta_refresh(3, $this->u_action);
+ $message = $user->lang['PREFERENCES_UPDATED'] . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $this->u_action . '">', '</a>');
+ trigger_error($message);
+ }
+
+ // Replace "error" strings with their real, localised form
+ $error = preg_replace('#^([A-Z_]+)$#e', "(!empty(\$user->lang['\\1'])) ? \$user->lang['\\1'] : '\\1'", $error);
+ }
+
+ $sort_dir_text = array('a' => $user->lang['ASCENDING'], 'd' => $user->lang['DESCENDING']);
+
+ // Topic ordering options
+ $limit_topic_days = array(0 => $user->lang['ALL_TOPICS'], 1 => $user->lang['1_DAY'], 7 => $user->lang['7_DAYS'], 14 => $user->lang['2_WEEKS'], 30 => $user->lang['1_MONTH'], 90 => $user->lang['3_MONTHS'], 180 => $user->lang['6_MONTHS'], 365 => $user->lang['1_YEAR']);
+
+ $sort_by_topic_text = array('a' => $user->lang['AUTHOR'], 't' => $user->lang['POST_TIME'], 'r' => $user->lang['REPLIES'], 's' => $user->lang['SUBJECT'], 'v' => $user->lang['VIEWS']);
+ $sort_by_topic_sql = array('a' => 't.topic_first_poster_name', 't' => 't.topic_last_post_time', 'r' => 't.topic_replies', 's' => 't.topic_title', 'v' => 't.topic_views');
+
+ // Post ordering options
+ $limit_post_days = array(0 => $user->lang['ALL_POSTS'], 1 => $user->lang['1_DAY'], 7 => $user->lang['7_DAYS'], 14 => $user->lang['2_WEEKS'], 30 => $user->lang['1_MONTH'], 90 => $user->lang['3_MONTHS'], 180 => $user->lang['6_MONTHS'], 365 => $user->lang['1_YEAR']);
+
+ $sort_by_post_text = array('a' => $user->lang['AUTHOR'], 't' => $user->lang['POST_TIME'], 's' => $user->lang['SUBJECT']);
+ $sort_by_post_sql = array('a' => 'u.username_clean', 't' => 'p.post_id', 's' => 'p.post_subject');
+
+ $_options = array('topic', 'post');
+ foreach ($_options as $sort_option)
+ {
+ ${'s_limit_' . $sort_option . '_days'} = '<select name="' . $sort_option . '_st">';
+ foreach (${'limit_' . $sort_option . '_days'} as $day => $text)
+ {
+ $selected = ($data[$sort_option . '_st'] == $day) ? ' selected="selected"' : '';
+ ${'s_limit_' . $sort_option . '_days'} .= '<option value="' . $day . '"' . $selected . '>' . $text . '</option>';
+ }
+ ${'s_limit_' . $sort_option . '_days'} .= '</select>';
+
+ ${'s_sort_' . $sort_option . '_key'} = '<select name="' . $sort_option . '_sk">';
+ foreach (${'sort_by_' . $sort_option . '_text'} as $key => $text)
+ {
+ $selected = ($data[$sort_option . '_sk'] == $key) ? ' selected="selected"' : '';
+ ${'s_sort_' . $sort_option . '_key'} .= '<option value="' . $key . '"' . $selected . '>' . $text . '</option>';
+ }
+ ${'s_sort_' . $sort_option . '_key'} .= '</select>';
+
+ ${'s_sort_' . $sort_option . '_dir'} = '<select name="' . $sort_option . '_sd">';
+ foreach ($sort_dir_text as $key => $value)
+ {
+ $selected = ($data[$sort_option . '_sd'] == $key) ? ' selected="selected"' : '';
+ ${'s_sort_' . $sort_option . '_dir'} .= '<option value="' . $key . '"' . $selected . '>' . $value . '</option>';
+ }
+ ${'s_sort_' . $sort_option . '_dir'} .= '</select>';
+ }
+
+ $template->assign_vars(array(
+ 'ERROR' => (sizeof($error)) ? implode('<br />', $error) : '',
+
+ 'S_IMAGES' => $data['images'],
+ 'S_FLASH' => $data['flash'],
+ 'S_SMILIES' => $data['smilies'],
+ 'S_SIGS' => $data['sigs'],
+ 'S_AVATARS' => $data['avatars'],
+ 'S_DISABLE_CENSORS' => $data['wordcensor'],
+
+ 'S_CHANGE_CENSORS' => ($auth->acl_get('u_chgcensors') && $config['allow_nocensors']) ? true : false,
+
+ 'S_TOPIC_SORT_DAYS' => $s_limit_topic_days,
+ 'S_TOPIC_SORT_KEY' => $s_sort_topic_key,
+ 'S_TOPIC_SORT_DIR' => $s_sort_topic_dir,
+ 'S_POST_SORT_DAYS' => $s_limit_post_days,
+ 'S_POST_SORT_KEY' => $s_sort_post_key,
+ 'S_POST_SORT_DIR' => $s_sort_post_dir)
+ );
+
+ break;
+
+ case 'post':
+
+ $data = array(
+ 'bbcode' => request_var('bbcode', $user->optionget('bbcode')),
+ 'smilies' => request_var('smilies', $user->optionget('smilies')),
+ 'sig' => request_var('sig', $user->optionget('attachsig')),
+ 'notify' => request_var('notify', (bool) $user->data['user_notify']),
+ );
+ add_form_key('ucp_prefs_post');
+
+ if ($submit)
+ {
+ if (check_form_key('ucp_prefs_post'))
+ {
+ $user->optionset('bbcode', $data['bbcode']);
+ $user->optionset('smilies', $data['smilies']);
+ $user->optionset('attachsig', $data['sig']);
+
+ $sql_ary = array(
+ 'user_options' => $user->data['user_options'],
+ 'user_notify' => $data['notify'],
+ );
+
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
+ WHERE user_id = ' . $user->data['user_id'];
+ $db->sql_query($sql);
+
+ $msg = $user->lang['PREFERENCES_UPDATED'];
+ }
+ else
+ {
+ $msg = $user->lang['FORM_INVALID'];
+ }
+ meta_refresh(3, $this->u_action);
+ $message = $msg . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $this->u_action . '">', '</a>');
+ trigger_error($message);
+ }
+
+ $template->assign_vars(array(
+ 'S_BBCODE' => $data['bbcode'],
+ 'S_SMILIES' => $data['smilies'],
+ 'S_SIG' => $data['sig'],
+ 'S_NOTIFY' => $data['notify'])
+ );
+ break;
+ }
+
+ $template->assign_vars(array(
+ 'L_TITLE' => $user->lang['UCP_PREFS_' . strtoupper($mode)],
+
+ 'S_HIDDEN_FIELDS' => $s_hidden_fields,
+ 'S_UCP_ACTION' => $this->u_action)
+ );
+
+ $this->tpl_name = 'ucp_prefs_' . $mode;
+ $this->page_title = 'UCP_PREFS_' . strtoupper($mode);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/ucp_profile.php b/phpBB/includes/ucp/ucp_profile.php
new file mode 100644
index 0000000000..e24acd89fc
--- /dev/null
+++ b/phpBB/includes/ucp/ucp_profile.php
@@ -0,0 +1,649 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* ucp_profile
+* Changing profile settings
+*
+* @todo what about pertaining user_sig_options?
+* @package ucp
+*/
+class ucp_profile
+{
+ var $u_action;
+
+ function main($id, $mode)
+ {
+ global $config, $db, $user, $auth, $template, $phpbb_root_path, $phpEx;
+
+ $user->add_lang('posting');
+
+ $preview = (!empty($_POST['preview'])) ? true : false;
+ $submit = (!empty($_POST['submit'])) ? true : false;
+ $delete = (!empty($_POST['delete'])) ? true : false;
+ $error = $data = array();
+ $s_hidden_fields = '';
+
+ switch ($mode)
+ {
+ case 'reg_details':
+
+ $data = array(
+ 'username' => utf8_normalize_nfc(request_var('username', $user->data['username'], true)),
+ 'email' => strtolower(request_var('email', $user->data['user_email'])),
+ 'email_confirm' => strtolower(request_var('email_confirm', '')),
+ 'new_password' => request_var('new_password', '', true),
+ 'cur_password' => request_var('cur_password', '', true),
+ 'password_confirm' => request_var('password_confirm', '', true),
+ );
+
+ add_form_key('ucp_reg_details');
+
+ if ($submit)
+ {
+ // Do not check cur_password, it is the old one.
+ $check_ary = array(
+ 'new_password' => array(
+ array('string', true, $config['min_pass_chars'], $config['max_pass_chars']),
+ array('password')),
+ 'password_confirm' => array('string', true, $config['min_pass_chars'], $config['max_pass_chars']),
+ 'email' => array(
+ array('string', false, 6, 60),
+ array('email')),
+ 'email_confirm' => array('string', true, 6, 60),
+ );
+
+ if ($auth->acl_get('u_chgname') && $config['allow_namechange'])
+ {
+ $check_ary['username'] = array(
+ array('string', false, $config['min_name_chars'], $config['max_name_chars']),
+ array('username'),
+ );
+ }
+
+ $error = validate_data($data, $check_ary);
+
+ if ($auth->acl_get('u_chgpasswd') && $data['new_password'] && $data['password_confirm'] != $data['new_password'])
+ {
+ $error[] = 'NEW_PASSWORD_ERROR';
+ }
+
+ if (($data['new_password'] || ($auth->acl_get('u_chgemail') && $data['email'] != $user->data['user_email']) || ($data['username'] != $user->data['username'] && $auth->acl_get('u_chgname') && $config['allow_namechange'])) && !phpbb_check_hash($data['cur_password'], $user->data['user_password']))
+ {
+ $error[] = 'CUR_PASSWORD_ERROR';
+ }
+
+ // Only check the new password against the previous password if there have been no errors
+ if (!sizeof($error) && $auth->acl_get('u_chgpasswd') && $data['new_password'] && phpbb_check_hash($data['new_password'], $user->data['user_password']))
+ {
+ $error[] = 'SAME_PASSWORD_ERROR';
+ }
+
+ if ($auth->acl_get('u_chgemail') && $data['email'] != $user->data['user_email'] && $data['email_confirm'] != $data['email'])
+ {
+ $error[] = 'NEW_EMAIL_ERROR';
+ }
+
+ if (!check_form_key('ucp_reg_details'))
+ {
+ $error[] = 'FORM_INVALID';
+ }
+
+ if (!sizeof($error))
+ {
+ $sql_ary = array(
+ 'username' => ($auth->acl_get('u_chgname') && $config['allow_namechange']) ? $data['username'] : $user->data['username'],
+ 'username_clean' => ($auth->acl_get('u_chgname') && $config['allow_namechange']) ? utf8_clean_string($data['username']) : $user->data['username_clean'],
+ 'user_email' => ($auth->acl_get('u_chgemail')) ? $data['email'] : $user->data['user_email'],
+ 'user_email_hash' => ($auth->acl_get('u_chgemail')) ? phpbb_email_hash($data['email']) : $user->data['user_email_hash'],
+ 'user_password' => ($auth->acl_get('u_chgpasswd') && $data['new_password']) ? phpbb_hash($data['new_password']) : $user->data['user_password'],
+ 'user_passchg' => ($auth->acl_get('u_chgpasswd') && $data['new_password']) ? time() : 0,
+ );
+
+ if ($auth->acl_get('u_chgname') && $config['allow_namechange'] && $data['username'] != $user->data['username'])
+ {
+ add_log('user', $user->data['user_id'], 'LOG_USER_UPDATE_NAME', $user->data['username'], $data['username']);
+ }
+
+ if ($auth->acl_get('u_chgpasswd') && $data['new_password'] && !phpbb_check_hash($data['new_password'], $user->data['user_password']))
+ {
+ $user->reset_login_keys();
+ add_log('user', $user->data['user_id'], 'LOG_USER_NEW_PASSWORD', $data['username']);
+ }
+
+ if ($auth->acl_get('u_chgemail') && $data['email'] != $user->data['user_email'])
+ {
+ add_log('user', $user->data['user_id'], 'LOG_USER_UPDATE_EMAIL', $data['username'], $user->data['user_email'], $data['email']);
+ }
+
+ $message = 'PROFILE_UPDATED';
+
+ if ($config['email_enable'] && $data['email'] != $user->data['user_email'] && $user->data['user_type'] != USER_FOUNDER && ($config['require_activation'] == USER_ACTIVATION_SELF || $config['require_activation'] == USER_ACTIVATION_ADMIN))
+ {
+ $message = ($config['require_activation'] == USER_ACTIVATION_SELF) ? 'ACCOUNT_EMAIL_CHANGED' : 'ACCOUNT_EMAIL_CHANGED_ADMIN';
+
+ include_once($phpbb_root_path . 'includes/functions_messenger.' . $phpEx);
+
+ $server_url = generate_board_url();
+
+ $user_actkey = gen_rand_string(10);
+ $key_len = 54 - (strlen($server_url));
+ $key_len = ($key_len > 6) ? $key_len : 6;
+ $user_actkey = substr($user_actkey, 0, $key_len);
+
+ $messenger = new messenger(false);
+
+ $template_file = ($config['require_activation'] == USER_ACTIVATION_ADMIN) ? 'user_activate_inactive' : 'user_activate';
+ $messenger->template($template_file, $user->data['user_lang']);
+
+ $messenger->to($data['email'], $data['username']);
+
+ $messenger->headers('X-AntiAbuse: Board servername - ' . $config['server_name']);
+ $messenger->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']);
+ $messenger->headers('X-AntiAbuse: Username - ' . $user->data['username']);
+ $messenger->headers('X-AntiAbuse: User IP - ' . $user->ip);
+
+ $messenger->assign_vars(array(
+ 'USERNAME' => htmlspecialchars_decode($data['username']),
+ 'U_ACTIVATE' => "$server_url/ucp.$phpEx?mode=activate&u={$user->data['user_id']}&k=$user_actkey")
+ );
+
+ $messenger->send(NOTIFY_EMAIL);
+
+ if ($config['require_activation'] == USER_ACTIVATION_ADMIN)
+ {
+ // Grab an array of user_id's with a_user permissions ... these users can activate a user
+ $admin_ary = $auth->acl_get_list(false, 'a_user', false);
+ $admin_ary = (!empty($admin_ary[0]['a_user'])) ? $admin_ary[0]['a_user'] : array();
+
+ // Also include founders
+ $where_sql = ' WHERE user_type = ' . USER_FOUNDER;
+
+ if (sizeof($admin_ary))
+ {
+ $where_sql .= ' OR ' . $db->sql_in_set('user_id', $admin_ary);
+ }
+
+ $sql = 'SELECT user_id, username, user_email, user_lang, user_jabber, user_notify_type
+ FROM ' . USERS_TABLE . ' ' .
+ $where_sql;
+ $result = $db->sql_query($sql);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $messenger->template('admin_activate', $row['user_lang']);
+ $messenger->to($row['user_email'], $row['username']);
+ $messenger->im($row['user_jabber'], $row['username']);
+
+ $messenger->assign_vars(array(
+ 'USERNAME' => htmlspecialchars_decode($data['username']),
+ 'U_USER_DETAILS' => "$server_url/memberlist.$phpEx?mode=viewprofile&u={$user->data['user_id']}",
+ 'U_ACTIVATE' => "$server_url/ucp.$phpEx?mode=activate&u={$user->data['user_id']}&k=$user_actkey")
+ );
+
+ $messenger->send($row['user_notify_type']);
+ }
+ $db->sql_freeresult($result);
+ }
+
+ user_active_flip('deactivate', $user->data['user_id'], INACTIVE_PROFILE);
+
+ // Because we want the profile to be reactivated we set user_newpasswd to empty (else the reactivation will fail)
+ $sql_ary['user_actkey'] = $user_actkey;
+ $sql_ary['user_newpasswd'] = '';
+ }
+
+ if (sizeof($sql_ary))
+ {
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
+ WHERE user_id = ' . $user->data['user_id'];
+ $db->sql_query($sql);
+ }
+
+ // Need to update config, forum, topic, posting, messages, etc.
+ if ($data['username'] != $user->data['username'] && $auth->acl_get('u_chgname') && $config['allow_namechange'])
+ {
+ user_update_name($user->data['username'], $data['username']);
+ }
+
+ // Now, we can remove the user completely (kill the session) - NOT BEFORE!!!
+ if (!empty($sql_ary['user_actkey']))
+ {
+ meta_refresh(5, append_sid($phpbb_root_path . 'index.' . $phpEx));
+ $message = $user->lang[$message] . '<br /><br />' . sprintf($user->lang['RETURN_INDEX'], '<a href="' . append_sid($phpbb_root_path . 'index.' . $phpEx) . '">', '</a>');
+
+ // Because the user gets deactivated we log him out too, killing his session
+ $user->session_kill();
+ }
+ else
+ {
+ meta_refresh(3, $this->u_action);
+ $message = $user->lang[$message] . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $this->u_action . '">', '</a>');
+ }
+
+ trigger_error($message);
+ }
+
+ // Replace "error" strings with their real, localised form
+ $error = preg_replace('#^([A-Z_]+)$#e', "(!empty(\$user->lang['\\1'])) ? \$user->lang['\\1'] : '\\1'", $error);
+ }
+
+ $template->assign_vars(array(
+ 'ERROR' => (sizeof($error)) ? implode('<br />', $error) : '',
+
+ 'USERNAME' => $data['username'],
+ 'EMAIL' => $data['email'],
+ 'PASSWORD_CONFIRM' => $data['password_confirm'],
+ 'NEW_PASSWORD' => $data['new_password'],
+ 'CUR_PASSWORD' => '',
+
+ 'L_USERNAME_EXPLAIN' => sprintf($user->lang[$config['allow_name_chars'] . '_EXPLAIN'], $config['min_name_chars'], $config['max_name_chars']),
+ 'L_CHANGE_PASSWORD_EXPLAIN' => sprintf($user->lang[$config['pass_complex'] . '_EXPLAIN'], $config['min_pass_chars'], $config['max_pass_chars']),
+
+ 'S_FORCE_PASSWORD' => ($auth->acl_get('u_chgpasswd') && $config['chg_passforce'] && $user->data['user_passchg'] < time() - ($config['chg_passforce'] * 86400)) ? true : false,
+ 'S_CHANGE_USERNAME' => ($config['allow_namechange'] && $auth->acl_get('u_chgname')) ? true : false,
+ 'S_CHANGE_EMAIL' => ($auth->acl_get('u_chgemail')) ? true : false,
+ 'S_CHANGE_PASSWORD' => ($auth->acl_get('u_chgpasswd')) ? true : false)
+ );
+ break;
+
+ case 'profile_info':
+
+ include($phpbb_root_path . 'includes/functions_profile_fields.' . $phpEx);
+
+ $cp = new custom_profile();
+
+ $cp_data = $cp_error = array();
+
+ $data = array(
+ 'icq' => request_var('icq', $user->data['user_icq']),
+ 'aim' => request_var('aim', $user->data['user_aim']),
+ 'msn' => request_var('msn', $user->data['user_msnm']),
+ 'yim' => request_var('yim', $user->data['user_yim']),
+ 'jabber' => utf8_normalize_nfc(request_var('jabber', $user->data['user_jabber'], true)),
+ 'website' => request_var('website', $user->data['user_website']),
+ 'location' => utf8_normalize_nfc(request_var('location', $user->data['user_from'], true)),
+ 'occupation' => utf8_normalize_nfc(request_var('occupation', $user->data['user_occ'], true)),
+ 'interests' => utf8_normalize_nfc(request_var('interests', $user->data['user_interests'], true)),
+ );
+
+ if ($config['allow_birthdays'])
+ {
+ $data['bday_day'] = $data['bday_month'] = $data['bday_year'] = 0;
+
+ if ($user->data['user_birthday'])
+ {
+ list($data['bday_day'], $data['bday_month'], $data['bday_year']) = explode('-', $user->data['user_birthday']);
+ }
+
+ $data['bday_day'] = request_var('bday_day', $data['bday_day']);
+ $data['bday_month'] = request_var('bday_month', $data['bday_month']);
+ $data['bday_year'] = request_var('bday_year', $data['bday_year']);
+ $data['user_birthday'] = sprintf('%2d-%2d-%4d', $data['bday_day'], $data['bday_month'], $data['bday_year']);
+ }
+
+ add_form_key('ucp_profile_info');
+
+ if ($submit)
+ {
+ $validate_array = array(
+ 'icq' => array(
+ array('string', true, 3, 15),
+ array('match', true, '#^[0-9]+$#i')),
+ 'aim' => array('string', true, 3, 255),
+ 'msn' => array('string', true, 5, 255),
+ 'jabber' => array(
+ array('string', true, 5, 255),
+ array('jabber')),
+ 'yim' => array('string', true, 5, 255),
+ 'website' => array(
+ array('string', true, 12, 255),
+ array('match', true, '#^http[s]?://(.*?\.)*?[a-z0-9\-]+\.[a-z]{2,4}#i')),
+ 'location' => array('string', true, 2, 100),
+ 'occupation' => array('string', true, 2, 500),
+ 'interests' => array('string', true, 2, 500),
+ );
+
+ if ($config['allow_birthdays'])
+ {
+ $validate_array = array_merge($validate_array, array(
+ 'bday_day' => array('num', true, 1, 31),
+ 'bday_month' => array('num', true, 1, 12),
+ 'bday_year' => array('num', true, 1901, gmdate('Y', time()) + 50),
+ 'user_birthday' => array('date', true),
+ ));
+ }
+
+ $error = validate_data($data, $validate_array);
+
+ // validate custom profile fields
+ $cp->submit_cp_field('profile', $user->get_iso_lang_id(), $cp_data, $cp_error);
+
+ if (sizeof($cp_error))
+ {
+ $error = array_merge($error, $cp_error);
+ }
+
+ if (!check_form_key('ucp_profile_info'))
+ {
+ $error[] = 'FORM_INVALID';
+ }
+
+ if (!sizeof($error))
+ {
+ $data['notify'] = $user->data['user_notify_type'];
+
+ if ($data['notify'] == NOTIFY_IM && (!$config['jab_enable'] || !$data['jabber'] || !@extension_loaded('xml')))
+ {
+ // User has not filled in a jabber address (Or one of the modules is disabled or jabber is disabled)
+ // Disable notify by Jabber now for this user.
+ $data['notify'] = NOTIFY_EMAIL;
+ }
+
+ $sql_ary = array(
+ 'user_icq' => $data['icq'],
+ 'user_aim' => $data['aim'],
+ 'user_msnm' => $data['msn'],
+ 'user_yim' => $data['yim'],
+ 'user_jabber' => $data['jabber'],
+ 'user_website' => $data['website'],
+ 'user_from' => $data['location'],
+ 'user_occ' => $data['occupation'],
+ 'user_interests'=> $data['interests'],
+ 'user_notify_type' => $data['notify'],
+ );
+
+ if ($config['allow_birthdays'])
+ {
+ $sql_ary['user_birthday'] = $data['user_birthday'];
+ }
+
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
+ WHERE user_id = ' . $user->data['user_id'];
+ $db->sql_query($sql);
+
+ // Update Custom Fields
+ $cp->update_profile_field_data($user->data['user_id'], $cp_data);
+
+ meta_refresh(3, $this->u_action);
+ $message = $user->lang['PROFILE_UPDATED'] . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $this->u_action . '">', '</a>');
+ trigger_error($message);
+ }
+
+ // Replace "error" strings with their real, localised form
+ $error = preg_replace('#^([A-Z_]+)$#e', "(!empty(\$user->lang['\\1'])) ? \$user->lang['\\1'] : '\\1'", $error);
+ }
+
+ if ($config['allow_birthdays'])
+ {
+ $s_birthday_day_options = '<option value="0"' . ((!$data['bday_day']) ? ' selected="selected"' : '') . '>--</option>';
+ for ($i = 1; $i < 32; $i++)
+ {
+ $selected = ($i == $data['bday_day']) ? ' selected="selected"' : '';
+ $s_birthday_day_options .= "<option value=\"$i\"$selected>$i</option>";
+ }
+
+ $s_birthday_month_options = '<option value="0"' . ((!$data['bday_month']) ? ' selected="selected"' : '') . '>--</option>';
+ for ($i = 1; $i < 13; $i++)
+ {
+ $selected = ($i == $data['bday_month']) ? ' selected="selected"' : '';
+ $s_birthday_month_options .= "<option value=\"$i\"$selected>$i</option>";
+ }
+ $s_birthday_year_options = '';
+
+ $now = getdate();
+ $s_birthday_year_options = '<option value="0"' . ((!$data['bday_year']) ? ' selected="selected"' : '') . '>--</option>';
+ for ($i = $now['year'] - 100; $i <= $now['year']; $i++)
+ {
+ $selected = ($i == $data['bday_year']) ? ' selected="selected"' : '';
+ $s_birthday_year_options .= "<option value=\"$i\"$selected>$i</option>";
+ }
+ unset($now);
+
+ $template->assign_vars(array(
+ 'S_BIRTHDAY_DAY_OPTIONS' => $s_birthday_day_options,
+ 'S_BIRTHDAY_MONTH_OPTIONS' => $s_birthday_month_options,
+ 'S_BIRTHDAY_YEAR_OPTIONS' => $s_birthday_year_options,
+ 'S_BIRTHDAYS_ENABLED' => true,
+ ));
+ }
+
+ $template->assign_vars(array(
+ 'ERROR' => (sizeof($error)) ? implode('<br />', $error) : '',
+
+ 'ICQ' => $data['icq'],
+ 'YIM' => $data['yim'],
+ 'AIM' => $data['aim'],
+ 'MSN' => $data['msn'],
+ 'JABBER' => $data['jabber'],
+ 'WEBSITE' => $data['website'],
+ 'LOCATION' => $data['location'],
+ 'OCCUPATION'=> $data['occupation'],
+ 'INTERESTS' => $data['interests'],
+ ));
+
+ // Get additional profile fields and assign them to the template block var 'profile_fields'
+ $user->get_profile_fields($user->data['user_id']);
+
+ $cp->generate_profile_fields('profile', $user->get_iso_lang_id());
+
+ break;
+
+ case 'signature':
+
+ if (!$auth->acl_get('u_sig'))
+ {
+ trigger_error('NO_AUTH_SIGNATURE');
+ }
+
+ include($phpbb_root_path . 'includes/functions_posting.' . $phpEx);
+ include($phpbb_root_path . 'includes/functions_display.' . $phpEx);
+
+ $enable_bbcode = ($config['allow_sig_bbcode']) ? (bool) $user->optionget('sig_bbcode') : false;
+ $enable_smilies = ($config['allow_sig_smilies']) ? (bool) $user->optionget('sig_smilies') : false;
+ $enable_urls = ($config['allow_sig_links']) ? (bool) $user->optionget('sig_links') : false;
+
+ $signature = utf8_normalize_nfc(request_var('signature', (string) $user->data['user_sig'], true));
+
+ add_form_key('ucp_sig');
+
+ if ($submit || $preview)
+ {
+ include($phpbb_root_path . 'includes/message_parser.' . $phpEx);
+
+ $enable_bbcode = ($config['allow_sig_bbcode']) ? ((request_var('disable_bbcode', false)) ? false : true) : false;
+ $enable_smilies = ($config['allow_sig_smilies']) ? ((request_var('disable_smilies', false)) ? false : true) : false;
+ $enable_urls = ($config['allow_sig_links']) ? ((request_var('disable_magic_url', false)) ? false : true) : false;
+
+ if (!sizeof($error))
+ {
+ $message_parser = new parse_message($signature);
+
+ // Allowing Quote BBCode
+ $message_parser->parse($enable_bbcode, $enable_urls, $enable_smilies, $config['allow_sig_img'], $config['allow_sig_flash'], true, $config['allow_sig_links'], true, 'sig');
+
+ if (sizeof($message_parser->warn_msg))
+ {
+ $error[] = implode('<br />', $message_parser->warn_msg);
+ }
+
+ if (!check_form_key('ucp_sig'))
+ {
+ $error[] = 'FORM_INVALID';
+ }
+
+ if (!sizeof($error) && $submit)
+ {
+ $user->optionset('sig_bbcode', $enable_bbcode);
+ $user->optionset('sig_smilies', $enable_smilies);
+ $user->optionset('sig_links', $enable_urls);
+
+ $sql_ary = array(
+ 'user_sig' => (string) $message_parser->message,
+ 'user_options' => $user->data['user_options'],
+ 'user_sig_bbcode_uid' => (string) $message_parser->bbcode_uid,
+ 'user_sig_bbcode_bitfield' => $message_parser->bbcode_bitfield
+ );
+
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
+ WHERE user_id = ' . $user->data['user_id'];
+ $db->sql_query($sql);
+
+ $message = $user->lang['PROFILE_UPDATED'] . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $this->u_action . '">', '</a>');
+ trigger_error($message);
+ }
+ }
+
+ // Replace "error" strings with their real, localised form
+ $error = preg_replace('#^([A-Z_]+)$#e', "(!empty(\$user->lang['\\1'])) ? \$user->lang['\\1'] : '\\1'", $error);
+ }
+
+ $signature_preview = '';
+ if ($preview)
+ {
+ // Now parse it for displaying
+ $signature_preview = $message_parser->format_display($enable_bbcode, $enable_urls, $enable_smilies, false);
+ unset($message_parser);
+ }
+
+ decode_message($signature, $user->data['user_sig_bbcode_uid']);
+
+ $template->assign_vars(array(
+ 'ERROR' => (sizeof($error)) ? implode('<br />', $error) : '',
+ 'SIGNATURE' => $signature,
+ 'SIGNATURE_PREVIEW' => $signature_preview,
+
+ 'S_BBCODE_CHECKED' => (!$enable_bbcode) ? ' checked="checked"' : '',
+ 'S_SMILIES_CHECKED' => (!$enable_smilies) ? ' checked="checked"' : '',
+ 'S_MAGIC_URL_CHECKED' => (!$enable_urls) ? ' checked="checked"' : '',
+
+ 'BBCODE_STATUS' => ($config['allow_sig_bbcode']) ? sprintf($user->lang['BBCODE_IS_ON'], '<a href="' . append_sid("{$phpbb_root_path}faq.$phpEx", 'mode=bbcode') . '">', '</a>') : sprintf($user->lang['BBCODE_IS_OFF'], '<a href="' . append_sid("{$phpbb_root_path}faq.$phpEx", 'mode=bbcode') . '">', '</a>'),
+ 'SMILIES_STATUS' => ($config['allow_sig_smilies']) ? $user->lang['SMILIES_ARE_ON'] : $user->lang['SMILIES_ARE_OFF'],
+ 'IMG_STATUS' => ($config['allow_sig_img']) ? $user->lang['IMAGES_ARE_ON'] : $user->lang['IMAGES_ARE_OFF'],
+ 'FLASH_STATUS' => ($config['allow_sig_flash']) ? $user->lang['FLASH_IS_ON'] : $user->lang['FLASH_IS_OFF'],
+ 'URL_STATUS' => ($config['allow_sig_links']) ? $user->lang['URL_IS_ON'] : $user->lang['URL_IS_OFF'],
+ 'MAX_FONT_SIZE' => (int) $config['max_sig_font_size'],
+
+ 'L_SIGNATURE_EXPLAIN' => sprintf($user->lang['SIGNATURE_EXPLAIN'], $config['max_sig_chars']),
+
+ 'S_BBCODE_ALLOWED' => $config['allow_sig_bbcode'],
+ 'S_SMILIES_ALLOWED' => $config['allow_sig_smilies'],
+ 'S_BBCODE_IMG' => ($config['allow_sig_img']) ? true : false,
+ 'S_BBCODE_FLASH' => ($config['allow_sig_flash']) ? true : false,
+ 'S_LINKS_ALLOWED' => ($config['allow_sig_links']) ? true : false)
+ );
+
+ // Build custom bbcodes array
+ display_custom_bbcodes();
+
+ break;
+
+ case 'avatar':
+
+ include($phpbb_root_path . 'includes/functions_display.' . $phpEx);
+
+ $display_gallery = request_var('display_gallery', '0');
+ $avatar_select = basename(request_var('avatar_select', ''));
+ $category = basename(request_var('category', ''));
+
+ $can_upload = (file_exists($phpbb_root_path . $config['avatar_path']) && @is_writable($phpbb_root_path . $config['avatar_path']) && $auth->acl_get('u_chgavatar') && (@ini_get('file_uploads') || strtolower(@ini_get('file_uploads')) == 'on')) ? true : false;
+
+ add_form_key('ucp_avatar');
+
+ if ($submit)
+ {
+ if (check_form_key('ucp_avatar'))
+ {
+ if (avatar_process_user($error))
+ {
+ meta_refresh(3, $this->u_action);
+ $message = $user->lang['PROFILE_UPDATED'] . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $this->u_action . '">', '</a>');
+ trigger_error($message);
+ }
+ }
+ else
+ {
+ $error[] = 'FORM_INVALID';
+ }
+ // Replace "error" strings with their real, localised form
+ $error = preg_replace('#^([A-Z_]+)$#e', "(!empty(\$user->lang['\\1'])) ? \$user->lang['\\1'] : '\\1'", $error);
+ }
+
+ if (!$config['allow_avatar'] && $user->data['user_avatar_type'])
+ {
+ $error[] = $user->lang['AVATAR_NOT_ALLOWED'];
+ }
+ else if ((($user->data['user_avatar_type'] == AVATAR_UPLOAD) && !$config['allow_avatar_upload']) ||
+ (($user->data['user_avatar_type'] == AVATAR_REMOTE) && !$config['allow_avatar_remote']) ||
+ (($user->data['user_avatar_type'] == AVATAR_GALLERY) && !$config['allow_avatar_local']))
+ {
+ $error[] = $user->lang['AVATAR_TYPE_NOT_ALLOWED'];
+ }
+
+ $template->assign_vars(array(
+ 'ERROR' => (sizeof($error)) ? implode('<br />', $error) : '',
+ 'AVATAR' => get_user_avatar($user->data['user_avatar'], $user->data['user_avatar_type'], $user->data['user_avatar_width'], $user->data['user_avatar_height'], 'USER_AVATAR', true),
+ 'AVATAR_SIZE' => $config['avatar_filesize'],
+
+ 'U_GALLERY' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=profile&amp;mode=avatar&amp;display_gallery=1'),
+
+ 'S_FORM_ENCTYPE' => ($can_upload && ($config['allow_avatar_upload'] || $config['allow_avatar_remote_upload'])) ? ' enctype="multipart/form-data"' : '',
+
+ 'L_AVATAR_EXPLAIN' => sprintf($user->lang['AVATAR_EXPLAIN'], $config['avatar_max_width'], $config['avatar_max_height'], $config['avatar_filesize'] / 1024),
+ ));
+
+ if ($config['allow_avatar'] && $display_gallery && $auth->acl_get('u_chgavatar') && $config['allow_avatar_local'])
+ {
+ avatar_gallery($category, $avatar_select, 4);
+ }
+ else if ($config['allow_avatar'])
+ {
+ $avatars_enabled = (($can_upload && ($config['allow_avatar_upload'] || $config['allow_avatar_remote_upload'])) || ($auth->acl_get('u_chgavatar') && ($config['allow_avatar_local'] || $config['allow_avatar_remote']))) ? true : false;
+
+ $template->assign_vars(array(
+ 'AVATAR_WIDTH' => request_var('width', $user->data['user_avatar_width']),
+ 'AVATAR_HEIGHT' => request_var('height', $user->data['user_avatar_height']),
+
+ 'S_AVATARS_ENABLED' => $avatars_enabled,
+ 'S_UPLOAD_AVATAR_FILE' => ($can_upload && $config['allow_avatar_upload']) ? true : false,
+ 'S_UPLOAD_AVATAR_URL' => ($can_upload && $config['allow_avatar_remote_upload']) ? true : false,
+ 'S_LINK_AVATAR' => ($auth->acl_get('u_chgavatar') && $config['allow_avatar_remote']) ? true : false,
+ 'S_DISPLAY_GALLERY' => ($auth->acl_get('u_chgavatar') && $config['allow_avatar_local']) ? true : false)
+ );
+ }
+
+ break;
+ }
+
+ $template->assign_vars(array(
+ 'L_TITLE' => $user->lang['UCP_PROFILE_' . strtoupper($mode)],
+
+ 'S_HIDDEN_FIELDS' => $s_hidden_fields,
+ 'S_UCP_ACTION' => $this->u_action)
+ );
+
+ // Set desired template
+ $this->tpl_name = 'ucp_profile_' . $mode;
+ $this->page_title = 'UCP_PROFILE_' . strtoupper($mode);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/ucp_register.php b/phpBB/includes/ucp/ucp_register.php
new file mode 100644
index 0000000000..8359c223e0
--- /dev/null
+++ b/phpBB/includes/ucp/ucp_register.php
@@ -0,0 +1,503 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* ucp_register
+* Board registration
+* @package ucp
+*/
+class ucp_register
+{
+ var $u_action;
+
+ function main($id, $mode)
+ {
+ global $config, $db, $user, $auth, $template, $phpbb_root_path, $phpEx;
+
+ //
+ if ($config['require_activation'] == USER_ACTIVATION_DISABLE)
+ {
+ trigger_error('UCP_REGISTER_DISABLE');
+ }
+
+ include($phpbb_root_path . 'includes/functions_profile_fields.' . $phpEx);
+
+ $coppa = (isset($_REQUEST['coppa'])) ? ((!empty($_REQUEST['coppa'])) ? 1 : 0) : false;
+ $agreed = (!empty($_POST['agreed'])) ? 1 : 0;
+ $submit = (isset($_POST['submit'])) ? true : false;
+ $change_lang = request_var('change_lang', '');
+ $user_lang = request_var('lang', $user->lang_name);
+
+ if ($agreed)
+ {
+ add_form_key('ucp_register');
+ }
+ else
+ {
+ add_form_key('ucp_register_terms');
+ }
+
+ if ($change_lang || $user_lang != $config['default_lang'])
+ {
+ $use_lang = ($change_lang) ? basename($change_lang) : basename($user_lang);
+
+ if (file_exists($user->lang_path . $use_lang . '/'))
+ {
+ if ($change_lang)
+ {
+ $submit = false;
+
+ // Setting back agreed to let the user view the agreement in his/her language
+ $agreed = (empty($_GET['change_lang'])) ? 0 : $agreed;
+ }
+
+ $user->lang_name = $user_lang = $use_lang;
+ $user->lang = array();
+ $user->data['user_lang'] = $user->lang_name;
+ $user->add_lang(array('common', 'ucp'));
+ }
+ else
+ {
+ $change_lang = '';
+ $user_lang = $user->lang_name;
+ }
+ }
+
+
+ $cp = new custom_profile();
+
+ $error = $cp_data = $cp_error = array();
+
+ if (!$agreed || ($coppa === false && $config['coppa_enable']) || ($coppa && !$config['coppa_enable']))
+ {
+ $add_lang = ($change_lang) ? '&amp;change_lang=' . urlencode($change_lang) : '';
+ $add_coppa = ($coppa !== false) ? '&amp;coppa=' . $coppa : '';
+
+ $s_hidden_fields = array(
+ 'change_lang' => $change_lang,
+ );
+
+ // If we change the language, we want to pass on some more possible parameter.
+ if ($change_lang)
+ {
+ // We do not include the password
+ $s_hidden_fields = array_merge($s_hidden_fields, array(
+ 'username' => utf8_normalize_nfc(request_var('username', '', true)),
+ 'email' => strtolower(request_var('email', '')),
+ 'email_confirm' => strtolower(request_var('email_confirm', '')),
+ 'lang' => $user->lang_name,
+ 'tz' => request_var('tz', (float) $config['board_timezone']),
+ ));
+
+ }
+
+ // Checking amount of available languages
+ $sql = 'SELECT lang_id
+ FROM ' . LANG_TABLE;
+ $result = $db->sql_query($sql);
+
+ $lang_row = array();
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $lang_row[] = $row;
+ }
+ $db->sql_freeresult($result);
+
+ if ($coppa === false && $config['coppa_enable'])
+ {
+ $now = getdate();
+ $coppa_birthday = $user->format_date(mktime($now['hours'] + $user->data['user_dst'], $now['minutes'], $now['seconds'], $now['mon'], $now['mday'] - 1, $now['year'] - 13), $user->lang['DATE_FORMAT']);
+ unset($now);
+
+ $template->assign_vars(array(
+ 'S_LANG_OPTIONS' => (sizeof($lang_row) > 1) ? language_select($user_lang) : '',
+ 'L_COPPA_NO' => sprintf($user->lang['UCP_COPPA_BEFORE'], $coppa_birthday),
+ 'L_COPPA_YES' => sprintf($user->lang['UCP_COPPA_ON_AFTER'], $coppa_birthday),
+
+ 'U_COPPA_NO' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=register&amp;coppa=0' . $add_lang),
+ 'U_COPPA_YES' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=register&amp;coppa=1' . $add_lang),
+
+ 'S_SHOW_COPPA' => true,
+ 'S_HIDDEN_FIELDS' => build_hidden_fields($s_hidden_fields),
+ 'S_UCP_ACTION' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=register' . $add_lang),
+ ));
+ }
+ else
+ {
+ $template->assign_vars(array(
+ 'S_LANG_OPTIONS' => (sizeof($lang_row) > 1) ? language_select($user_lang) : '',
+ 'L_TERMS_OF_USE' => sprintf($user->lang['TERMS_OF_USE_CONTENT'], $config['sitename'], generate_board_url()),
+
+ 'S_SHOW_COPPA' => false,
+ 'S_REGISTRATION' => true,
+ 'S_HIDDEN_FIELDS' => build_hidden_fields($s_hidden_fields),
+ 'S_UCP_ACTION' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=register' . $add_lang . $add_coppa),
+ )
+ );
+ }
+ unset($lang_row);
+
+ $this->tpl_name = 'ucp_agreement';
+ return;
+ }
+
+
+ // The CAPTCHA kicks in here. We can't help that the information gets lost on language change.
+ if ($config['enable_confirm'])
+ {
+ include($phpbb_root_path . 'includes/captcha/captcha_factory.' . $phpEx);
+ $captcha =& phpbb_captcha_factory::get_instance($config['captcha_plugin']);
+ $captcha->init(CONFIRM_REG);
+ }
+
+ // Try to manually determine the timezone and adjust the dst if the server date/time complies with the default setting +/- 1
+ $timezone = date('Z') / 3600;
+ $is_dst = date('I');
+
+ if ($config['board_timezone'] == $timezone || $config['board_timezone'] == ($timezone - 1))
+ {
+ $timezone = ($is_dst) ? $timezone - 1 : $timezone;
+
+ if (!isset($user->lang['tz_zones'][(string) $timezone]))
+ {
+ $timezone = $config['board_timezone'];
+ }
+ }
+ else
+ {
+ $is_dst = $config['board_dst'];
+ $timezone = $config['board_timezone'];
+ }
+
+ $data = array(
+ 'username' => utf8_normalize_nfc(request_var('username', '', true)),
+ 'new_password' => request_var('new_password', '', true),
+ 'password_confirm' => request_var('password_confirm', '', true),
+ 'email' => strtolower(request_var('email', '')),
+ 'email_confirm' => strtolower(request_var('email_confirm', '')),
+ 'lang' => basename(request_var('lang', $user->lang_name)),
+ 'tz' => request_var('tz', (float) $timezone),
+ );
+
+ // Check and initialize some variables if needed
+ if ($submit)
+ {
+ $error = validate_data($data, array(
+ 'username' => array(
+ array('string', false, $config['min_name_chars'], $config['max_name_chars']),
+ array('username', '')),
+ 'new_password' => array(
+ array('string', false, $config['min_pass_chars'], $config['max_pass_chars']),
+ array('password')),
+ 'password_confirm' => array('string', false, $config['min_pass_chars'], $config['max_pass_chars']),
+ 'email' => array(
+ array('string', false, 6, 60),
+ array('email')),
+ 'email_confirm' => array('string', false, 6, 60),
+ 'tz' => array('num', false, -14, 14),
+ 'lang' => array('match', false, '#^[a-z_\-]{2,}$#i'),
+ ));
+
+ if (!check_form_key('ucp_register'))
+ {
+ $error[] = $user->lang['FORM_INVALID'];
+ }
+
+ // Replace "error" strings with their real, localised form
+ $error = preg_replace('#^([A-Z_]+)$#e', "(!empty(\$user->lang['\\1'])) ? \$user->lang['\\1'] : '\\1'", $error);
+
+ if ($config['enable_confirm'])
+ {
+ $vc_response = $captcha->validate($data);
+ if ($vc_response !== false)
+ {
+ $error[] = $vc_response;
+ }
+
+ if ($config['max_reg_attempts'] && $captcha->get_attempt_count() > $config['max_reg_attempts'])
+ {
+ $error[] = $user->lang['TOO_MANY_REGISTERS'];
+ }
+ }
+
+ // DNSBL check
+ if ($config['check_dnsbl'])
+ {
+ if (($dnsbl = $user->check_dnsbl('register')) !== false)
+ {
+ $error[] = sprintf($user->lang['IP_BLACKLISTED'], $user->ip, $dnsbl[1]);
+ }
+ }
+
+ // validate custom profile fields
+ $cp->submit_cp_field('register', $user->get_iso_lang_id(), $cp_data, $error);
+
+ if (!sizeof($error))
+ {
+ if ($data['new_password'] != $data['password_confirm'])
+ {
+ $error[] = $user->lang['NEW_PASSWORD_ERROR'];
+ }
+
+ if ($data['email'] != $data['email_confirm'])
+ {
+ $error[] = $user->lang['NEW_EMAIL_ERROR'];
+ }
+ }
+
+ if (!sizeof($error))
+ {
+ $server_url = generate_board_url();
+
+ // Which group by default?
+ $group_name = ($coppa) ? 'REGISTERED_COPPA' : 'REGISTERED';
+
+ $sql = 'SELECT group_id
+ FROM ' . GROUPS_TABLE . "
+ WHERE group_name = '" . $db->sql_escape($group_name) . "'
+ AND group_type = " . GROUP_SPECIAL;
+ $result = $db->sql_query($sql);
+ $row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if (!$row)
+ {
+ trigger_error('NO_GROUP');
+ }
+
+ $group_id = $row['group_id'];
+
+ if (($coppa ||
+ $config['require_activation'] == USER_ACTIVATION_SELF ||
+ $config['require_activation'] == USER_ACTIVATION_ADMIN) && $config['email_enable'])
+ {
+ $user_actkey = gen_rand_string(10);
+ $key_len = 54 - (strlen($server_url));
+ $key_len = ($key_len < 6) ? 6 : $key_len;
+ $user_actkey = substr($user_actkey, 0, $key_len);
+
+ $user_type = USER_INACTIVE;
+ $user_inactive_reason = INACTIVE_REGISTER;
+ $user_inactive_time = time();
+ }
+ else
+ {
+ $user_type = USER_NORMAL;
+ $user_actkey = '';
+ $user_inactive_reason = 0;
+ $user_inactive_time = 0;
+ }
+
+ $user_row = array(
+ 'username' => $data['username'],
+ 'user_password' => phpbb_hash($data['new_password']),
+ 'user_email' => $data['email'],
+ 'group_id' => (int) $group_id,
+ 'user_timezone' => (float) $data['tz'],
+ 'user_dst' => $is_dst,
+ 'user_lang' => $data['lang'],
+ 'user_type' => $user_type,
+ 'user_actkey' => $user_actkey,
+ 'user_ip' => $user->ip,
+ 'user_regdate' => time(),
+ 'user_inactive_reason' => $user_inactive_reason,
+ 'user_inactive_time' => $user_inactive_time,
+ );
+
+ if ($config['new_member_post_limit'])
+ {
+ $user_row['user_new'] = 1;
+ }
+
+ // Register user...
+ $user_id = user_add($user_row, $cp_data);
+
+ // This should not happen, because the required variables are listed above...
+ if ($user_id === false)
+ {
+ trigger_error('NO_USER', E_USER_ERROR);
+ }
+
+ if ($coppa && $config['email_enable'])
+ {
+ $message = $user->lang['ACCOUNT_COPPA'];
+ $email_template = 'coppa_welcome_inactive';
+ }
+ else if ($config['require_activation'] == USER_ACTIVATION_SELF && $config['email_enable'])
+ {
+ $message = $user->lang['ACCOUNT_INACTIVE'];
+ $email_template = 'user_welcome_inactive';
+ }
+ else if ($config['require_activation'] == USER_ACTIVATION_ADMIN && $config['email_enable'])
+ {
+ $message = $user->lang['ACCOUNT_INACTIVE_ADMIN'];
+ $email_template = 'admin_welcome_inactive';
+ }
+ else
+ {
+ $message = $user->lang['ACCOUNT_ADDED'];
+ $email_template = 'user_welcome';
+ }
+
+ if ($config['email_enable'])
+ {
+ include_once($phpbb_root_path . 'includes/functions_messenger.' . $phpEx);
+
+ $messenger = new messenger(false);
+
+ $messenger->template($email_template, $data['lang']);
+
+ $messenger->to($data['email'], $data['username']);
+
+ $messenger->headers('X-AntiAbuse: Board servername - ' . $config['server_name']);
+ $messenger->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']);
+ $messenger->headers('X-AntiAbuse: Username - ' . $user->data['username']);
+ $messenger->headers('X-AntiAbuse: User IP - ' . $user->ip);
+
+ $messenger->assign_vars(array(
+ 'WELCOME_MSG' => htmlspecialchars_decode(sprintf($user->lang['WELCOME_SUBJECT'], $config['sitename'])),
+ 'USERNAME' => htmlspecialchars_decode($data['username']),
+ 'PASSWORD' => htmlspecialchars_decode($data['new_password']),
+ 'U_ACTIVATE' => "$server_url/ucp.$phpEx?mode=activate&u=$user_id&k=$user_actkey")
+ );
+
+ if ($coppa)
+ {
+ $messenger->assign_vars(array(
+ 'FAX_INFO' => $config['coppa_fax'],
+ 'MAIL_INFO' => $config['coppa_mail'],
+ 'EMAIL_ADDRESS' => $data['email'])
+ );
+ }
+
+ $messenger->send(NOTIFY_EMAIL);
+
+ if ($config['require_activation'] == USER_ACTIVATION_ADMIN)
+ {
+ // Grab an array of user_id's with a_user permissions ... these users can activate a user
+ $admin_ary = $auth->acl_get_list(false, 'a_user', false);
+ $admin_ary = (!empty($admin_ary[0]['a_user'])) ? $admin_ary[0]['a_user'] : array();
+
+ // Also include founders
+ $where_sql = ' WHERE user_type = ' . USER_FOUNDER;
+
+ if (sizeof($admin_ary))
+ {
+ $where_sql .= ' OR ' . $db->sql_in_set('user_id', $admin_ary);
+ }
+
+ $sql = 'SELECT user_id, username, user_email, user_lang, user_jabber, user_notify_type
+ FROM ' . USERS_TABLE . ' ' .
+ $where_sql;
+ $result = $db->sql_query($sql);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $messenger->template('admin_activate', $row['user_lang']);
+ $messenger->to($row['user_email'], $row['username']);
+ $messenger->im($row['user_jabber'], $row['username']);
+
+ $messenger->assign_vars(array(
+ 'USERNAME' => htmlspecialchars_decode($data['username']),
+ 'U_USER_DETAILS' => "$server_url/memberlist.$phpEx?mode=viewprofile&u=$user_id",
+ 'U_ACTIVATE' => "$server_url/ucp.$phpEx?mode=activate&u=$user_id&k=$user_actkey")
+ );
+
+ $messenger->send($row['user_notify_type']);
+ }
+ $db->sql_freeresult($result);
+ }
+ }
+
+ $message = $message . '<br /><br />' . sprintf($user->lang['RETURN_INDEX'], '<a href="' . append_sid("{$phpbb_root_path}index.$phpEx") . '">', '</a>');
+ trigger_error($message);
+ }
+ }
+
+ $s_hidden_fields = array(
+ 'agreed' => 'true',
+ 'change_lang' => 0,
+ );
+
+ if ($config['coppa_enable'])
+ {
+ $s_hidden_fields['coppa'] = $coppa;
+ }
+
+ if ($config['enable_confirm'])
+ {
+ $s_hidden_fields = array_merge($s_hidden_fields, $captcha->get_hidden_fields());
+ }
+ $s_hidden_fields = build_hidden_fields($s_hidden_fields);
+ $confirm_image = '';
+
+ // Visual Confirmation - Show images
+ if ($config['enable_confirm'])
+ {
+ $template->assign_vars(array(
+ 'CAPTCHA_TEMPLATE' => $captcha->get_template(),
+ ));
+ }
+
+ //
+ $l_reg_cond = '';
+ switch ($config['require_activation'])
+ {
+ case USER_ACTIVATION_SELF:
+ $l_reg_cond = $user->lang['UCP_EMAIL_ACTIVATE'];
+ break;
+
+ case USER_ACTIVATION_ADMIN:
+ $l_reg_cond = $user->lang['UCP_ADMIN_ACTIVATE'];
+ break;
+ }
+
+ $template->assign_vars(array(
+ 'ERROR' => (sizeof($error)) ? implode('<br />', $error) : '',
+ 'USERNAME' => $data['username'],
+ 'PASSWORD' => $data['new_password'],
+ 'PASSWORD_CONFIRM' => $data['password_confirm'],
+ 'EMAIL' => $data['email'],
+ 'EMAIL_CONFIRM' => $data['email_confirm'],
+
+ 'L_REG_COND' => $l_reg_cond,
+ 'L_USERNAME_EXPLAIN' => sprintf($user->lang[$config['allow_name_chars'] . '_EXPLAIN'], $config['min_name_chars'], $config['max_name_chars']),
+ 'L_PASSWORD_EXPLAIN' => sprintf($user->lang[$config['pass_complex'] . '_EXPLAIN'], $config['min_pass_chars'], $config['max_pass_chars']),
+
+ 'S_LANG_OPTIONS' => language_select($data['lang']),
+ 'S_TZ_OPTIONS' => tz_select($data['tz']),
+ 'S_CONFIRM_REFRESH' => ($config['enable_confirm'] && $config['confirm_refresh']) ? true : false,
+ 'S_REGISTRATION' => true,
+ 'S_COPPA' => $coppa,
+ 'S_HIDDEN_FIELDS' => $s_hidden_fields,
+ 'S_UCP_ACTION' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=register'),
+ ));
+
+ //
+ $user->profile_fields = array();
+
+ // Generate profile fields -> Template Block Variable profile_fields
+ $cp->generate_profile_fields('register', $user->get_iso_lang_id());
+
+ //
+ $this->tpl_name = 'ucp_register';
+ $this->page_title = 'UCP_REGISTRATION';
+ }
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/ucp_remind.php b/phpBB/includes/ucp/ucp_remind.php
new file mode 100644
index 0000000000..df6733d038
--- /dev/null
+++ b/phpBB/includes/ucp/ucp_remind.php
@@ -0,0 +1,125 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* ucp_remind
+* Sending password reminders
+* @package ucp
+*/
+class ucp_remind
+{
+ var $u_action;
+
+ function main($id, $mode)
+ {
+ global $config, $phpbb_root_path, $phpEx;
+ global $db, $user, $auth, $template;
+
+ $username = request_var('username', '', true);
+ $email = strtolower(request_var('email', ''));
+ $submit = (isset($_POST['submit'])) ? true : false;
+
+ if ($submit)
+ {
+ $sql = 'SELECT user_id, username, user_permissions, user_email, user_jabber, user_notify_type, user_type, user_lang, user_inactive_reason
+ FROM ' . USERS_TABLE . "
+ WHERE user_email_hash = '" . $db->sql_escape(phpbb_email_hash($email)) . "'
+ AND username_clean = '" . $db->sql_escape(utf8_clean_string($username)) . "'";
+ $result = $db->sql_query($sql);
+ $user_row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if (!$user_row)
+ {
+ trigger_error('NO_EMAIL_USER');
+ }
+
+ if ($user_row['user_type'] == USER_IGNORE)
+ {
+ trigger_error('NO_USER');
+ }
+
+ if ($user_row['user_type'] == USER_INACTIVE)
+ {
+ if ($user_row['user_inactive_reason'] == INACTIVE_MANUAL)
+ {
+ trigger_error('ACCOUNT_DEACTIVATED');
+ }
+ else
+ {
+ trigger_error('ACCOUNT_NOT_ACTIVATED');
+ }
+ }
+
+ // Check users permissions
+ $auth2 = new auth();
+ $auth2->acl($user_row);
+
+ if (!$auth2->acl_get('u_chgpasswd'))
+ {
+ trigger_error('NO_AUTH_PASSWORD_REMINDER');
+ }
+
+ $server_url = generate_board_url();
+
+ $key_len = 54 - strlen($server_url);
+ $key_len = max(6, $key_len); // we want at least 6
+ $key_len = ($config['max_pass_chars']) ? min($key_len, $config['max_pass_chars']) : $key_len; // we want at most $config['max_pass_chars']
+ $user_actkey = substr(gen_rand_string(10), 0, $key_len);
+ $user_password = gen_rand_string(8);
+
+ $sql = 'UPDATE ' . USERS_TABLE . "
+ SET user_newpasswd = '" . $db->sql_escape(phpbb_hash($user_password)) . "', user_actkey = '" . $db->sql_escape($user_actkey) . "'
+ WHERE user_id = " . $user_row['user_id'];
+ $db->sql_query($sql);
+
+ include_once($phpbb_root_path . 'includes/functions_messenger.' . $phpEx);
+
+ $messenger = new messenger(false);
+
+ $messenger->template('user_activate_passwd', $user_row['user_lang']);
+
+ $messenger->to($user_row['user_email'], $user_row['username']);
+ $messenger->im($user_row['user_jabber'], $user_row['username']);
+
+ $messenger->assign_vars(array(
+ 'USERNAME' => htmlspecialchars_decode($user_row['username']),
+ 'PASSWORD' => htmlspecialchars_decode($user_password),
+ 'U_ACTIVATE' => "$server_url/ucp.$phpEx?mode=activate&u={$user_row['user_id']}&k=$user_actkey")
+ );
+
+ $messenger->send($user_row['user_notify_type']);
+
+ meta_refresh(3, append_sid("{$phpbb_root_path}index.$phpEx"));
+
+ $message = $user->lang['PASSWORD_UPDATED'] . '<br /><br />' . sprintf($user->lang['RETURN_INDEX'], '<a href="' . append_sid("{$phpbb_root_path}index.$phpEx") . '">', '</a>');
+ trigger_error($message);
+ }
+
+ $template->assign_vars(array(
+ 'USERNAME' => $username,
+ 'EMAIL' => $email,
+ 'S_PROFILE_ACTION' => append_sid($phpbb_root_path . 'ucp.' . $phpEx, 'mode=sendpassword'))
+ );
+
+ $this->tpl_name = 'ucp_remind';
+ $this->page_title = 'UCP_REMIND';
+ }
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/ucp_resend.php b/phpBB/includes/ucp/ucp_resend.php
new file mode 100644
index 0000000000..39e9be24a1
--- /dev/null
+++ b/phpBB/includes/ucp/ucp_resend.php
@@ -0,0 +1,170 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* ucp_resend
+* Resending activation emails
+* @package ucp
+*/
+class ucp_resend
+{
+ var $u_action;
+
+ function main($id, $mode)
+ {
+ global $config, $phpbb_root_path, $phpEx;
+ global $db, $user, $auth, $template;
+
+ $username = request_var('username', '', true);
+ $email = strtolower(request_var('email', ''));
+ $submit = (isset($_POST['submit'])) ? true : false;
+
+ add_form_key('ucp_resend');
+
+ if ($submit)
+ {
+ if (!check_form_key('ucp_resend'))
+ {
+ trigger_error('FORM_INVALID');
+ }
+
+ $sql = 'SELECT user_id, group_id, username, user_email, user_type, user_lang, user_actkey, user_inactive_reason
+ FROM ' . USERS_TABLE . "
+ WHERE user_email_hash = '" . $db->sql_escape(phpbb_email_hash($email)) . "'
+ AND username_clean = '" . $db->sql_escape(utf8_clean_string($username)) . "'";
+ $result = $db->sql_query($sql);
+ $user_row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if (!$user_row)
+ {
+ trigger_error('NO_EMAIL_USER');
+ }
+
+ if ($user_row['user_type'] == USER_IGNORE)
+ {
+ trigger_error('NO_USER');
+ }
+
+ if (!$user_row['user_actkey'] && $user_row['user_type'] != USER_INACTIVE)
+ {
+ trigger_error('ACCOUNT_ALREADY_ACTIVATED');
+ }
+
+ if (!$user_row['user_actkey'] || ($user_row['user_type'] == USER_INACTIVE && $user_row['user_inactive_reason'] == INACTIVE_MANUAL))
+ {
+ trigger_error('ACCOUNT_DEACTIVATED');
+ }
+
+ // Determine coppa status on group (REGISTERED(_COPPA))
+ $sql = 'SELECT group_name, group_type
+ FROM ' . GROUPS_TABLE . '
+ WHERE group_id = ' . $user_row['group_id'];
+ $result = $db->sql_query($sql);
+ $row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if (!$row)
+ {
+ trigger_error('NO_GROUP');
+ }
+
+ $coppa = ($row['group_name'] == 'REGISTERED_COPPA' && $row['group_type'] == GROUP_SPECIAL) ? true : false;
+
+ include_once($phpbb_root_path . 'includes/functions_messenger.' . $phpEx);
+ $messenger = new messenger(false);
+
+ if ($config['require_activation'] == USER_ACTIVATION_SELF || $coppa)
+ {
+ $messenger->template(($coppa) ? 'coppa_resend_inactive' : 'user_resend_inactive', $user_row['user_lang']);
+ $messenger->to($user_row['user_email'], $user_row['username']);
+
+ $messenger->headers('X-AntiAbuse: Board servername - ' . $config['server_name']);
+ $messenger->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']);
+ $messenger->headers('X-AntiAbuse: Username - ' . $user->data['username']);
+ $messenger->headers('X-AntiAbuse: User IP - ' . $user->ip);
+
+ $messenger->assign_vars(array(
+ 'WELCOME_MSG' => htmlspecialchars_decode(sprintf($user->lang['WELCOME_SUBJECT'], $config['sitename'])),
+ 'USERNAME' => htmlspecialchars_decode($user_row['username']),
+ 'U_ACTIVATE' => generate_board_url() . "/ucp.$phpEx?mode=activate&u={$user_row['user_id']}&k={$user_row['user_actkey']}")
+ );
+
+ if ($coppa)
+ {
+ $messenger->assign_vars(array(
+ 'FAX_INFO' => $config['coppa_fax'],
+ 'MAIL_INFO' => $config['coppa_mail'],
+ 'EMAIL_ADDRESS' => $user_row['user_email'])
+ );
+ }
+
+ $messenger->send(NOTIFY_EMAIL);
+ }
+
+ if ($config['require_activation'] == USER_ACTIVATION_ADMIN)
+ {
+ // Grab an array of user_id's with a_user permissions ... these users can activate a user
+ $admin_ary = $auth->acl_get_list(false, 'a_user', false);
+
+ $sql = 'SELECT user_id, username, user_email, user_lang, user_jabber, user_notify_type
+ FROM ' . USERS_TABLE . '
+ WHERE ' . $db->sql_in_set('user_id', $admin_ary[0]['a_user']);
+ $result = $db->sql_query($sql);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $messenger->template('admin_activate', $row['user_lang']);
+ $messenger->to($row['user_email'], $row['username']);
+ $messenger->im($row['user_jabber'], $row['username']);
+
+ $messenger->headers('X-AntiAbuse: Board servername - ' . $config['server_name']);
+ $messenger->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']);
+ $messenger->headers('X-AntiAbuse: Username - ' . $user->data['username']);
+ $messenger->headers('X-AntiAbuse: User IP - ' . $user->ip);
+
+ $messenger->assign_vars(array(
+ 'USERNAME' => htmlspecialchars_decode($user_row['username']),
+ 'U_USER_DETAILS' => generate_board_url() . "/memberlist.$phpEx?mode=viewprofile&u={$user_row['user_id']}",
+ 'U_ACTIVATE' => generate_board_url() . "/ucp.$phpEx?mode=activate&u={$user_row['user_id']}&k={$user_row['user_actkey']}")
+ );
+
+ $messenger->send($row['user_notify_type']);
+ }
+ $db->sql_freeresult($result);
+ }
+
+ meta_refresh(3, append_sid("{$phpbb_root_path}index.$phpEx"));
+
+ $message = ($config['require_activation'] == USER_ACTIVATION_ADMIN) ? $user->lang['ACTIVATION_EMAIL_SENT_ADMIN'] : $user->lang['ACTIVATION_EMAIL_SENT'];
+ $message .= '<br /><br />' . sprintf($user->lang['RETURN_INDEX'], '<a href="' . append_sid("{$phpbb_root_path}index.$phpEx") . '">', '</a>');
+ trigger_error($message);
+ }
+
+ $template->assign_vars(array(
+ 'USERNAME' => $username,
+ 'EMAIL' => $email,
+ 'S_PROFILE_ACTION' => append_sid($phpbb_root_path . 'ucp.' . $phpEx, 'mode=resend_act'))
+ );
+
+ $this->tpl_name = 'ucp_resend';
+ $this->page_title = 'UCP_RESEND';
+ }
+}
+
+?> \ No newline at end of file
diff --git a/phpBB/includes/ucp/ucp_zebra.php b/phpBB/includes/ucp/ucp_zebra.php
new file mode 100644
index 0000000000..5ed4db7520
--- /dev/null
+++ b/phpBB/includes/ucp/ucp_zebra.php
@@ -0,0 +1,257 @@
+<?php
+/**
+*
+* @package ucp
+* @version $Id$
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* ucp_zebra
+* @package ucp
+*/
+class ucp_zebra
+{
+ var $u_action;
+
+ function main($id, $mode)
+ {
+ global $config, $db, $user, $auth, $template, $phpbb_root_path, $phpEx;
+
+ $submit = (isset($_POST['submit']) || isset($_GET['add']) || isset($_GET['remove'])) ? true : false;
+ $s_hidden_fields = '';
+
+ $l_mode = strtoupper($mode);
+
+ if ($submit)
+ {
+ $data = $error = array();
+ $updated = false;
+
+ $var_ary = array(
+ 'usernames' => array(0),
+ 'add' => '',
+ );
+
+ foreach ($var_ary as $var => $default)
+ {
+ $data[$var] = request_var($var, $default, true);
+ }
+
+ if (!empty($data['add']) || sizeof($data['usernames']))
+ {
+ if (confirm_box(true))
+ {
+ // Remove users
+ if (!empty($data['usernames']))
+ {
+ $sql = 'DELETE FROM ' . ZEBRA_TABLE . '
+ WHERE user_id = ' . $user->data['user_id'] . '
+ AND ' . $db->sql_in_set('zebra_id', $data['usernames']);
+ $db->sql_query($sql);
+
+ $updated = true;
+ }
+
+ // Add users
+ if ($data['add'])
+ {
+ $data['add'] = array_map('trim', array_map('utf8_clean_string', explode("\n", $data['add'])));
+
+ // Do these name/s exist on a list already? If so, ignore ... we could be
+ // 'nice' and automatically handle names added to one list present on
+ // the other (by removing the existing one) ... but I have a feeling this
+ // may lead to complaints
+ $sql = 'SELECT z.*, u.username, u.username_clean
+ FROM ' . ZEBRA_TABLE . ' z, ' . USERS_TABLE . ' u
+ WHERE z.user_id = ' . $user->data['user_id'] . '
+ AND u.user_id = z.zebra_id';
+ $result = $db->sql_query($sql);
+
+ $friends = $foes = array();
+ while ($row = $db->sql_fetchrow($result))
+ {
+ if ($row['friend'])
+ {
+ $friends[] = utf8_clean_string($row['username']);
+ }
+ else
+ {
+ $foes[] = utf8_clean_string($row['username']);
+ }
+ }
+ $db->sql_freeresult($result);
+
+ // remove friends from the username array
+ $n = sizeof($data['add']);
+ $data['add'] = array_diff($data['add'], $friends);
+
+ if (sizeof($data['add']) < $n && $mode == 'foes')
+ {
+ $error[] = $user->lang['NOT_ADDED_FOES_FRIENDS'];
+ }
+
+ // remove foes from the username array
+ $n = sizeof($data['add']);
+ $data['add'] = array_diff($data['add'], $foes);
+
+ if (sizeof($data['add']) < $n && $mode == 'friends')
+ {
+ $error[] = $user->lang['NOT_ADDED_FRIENDS_FOES'];
+ }
+
+ // remove the user himself from the username array
+ $n = sizeof($data['add']);
+ $data['add'] = array_diff($data['add'], array(utf8_clean_string($user->data['username'])));
+
+ if (sizeof($data['add']) < $n)
+ {
+ $error[] = $user->lang['NOT_ADDED_' . $l_mode . '_SELF'];
+ }
+
+ unset($friends, $foes, $n);
+
+ if (sizeof($data['add']))
+ {
+ $sql = 'SELECT user_id, user_type
+ FROM ' . USERS_TABLE . '
+ WHERE ' . $db->sql_in_set('username_clean', $data['add']) . '
+ AND user_type <> ' . USER_INACTIVE;
+ $result = $db->sql_query($sql);
+
+ $user_id_ary = array();
+ while ($row = $db->sql_fetchrow($result))
+ {
+ if ($row['user_id'] != ANONYMOUS && $row['user_type'] != USER_IGNORE)
+ {
+ $user_id_ary[] = $row['user_id'];
+ }
+ else if ($row['user_id'] != ANONYMOUS)
+ {
+ $error[] = $user->lang['NOT_ADDED_' . $l_mode . '_BOTS'];
+ }
+ else
+ {
+ $error[] = $user->lang['NOT_ADDED_' . $l_mode . '_ANONYMOUS'];
+ }
+ }
+ $db->sql_freeresult($result);
+
+ if (sizeof($user_id_ary))
+ {
+ // Remove users from foe list if they are admins or moderators
+ if ($mode == 'foes')
+ {
+ $perms = array();
+ foreach ($auth->acl_get_list($user_id_ary, array('a_', 'm_')) as $forum_id => $forum_ary)
+ {
+ foreach ($forum_ary as $auth_option => $user_ary)
+ {
+ $perms = array_merge($perms, $user_ary);
+ }
+ }
+
+ $perms = array_unique($perms);
+
+ if (sizeof($perms))
+ {
+ $error[] = $user->lang['NOT_ADDED_FOES_MOD_ADMIN'];
+ }
+
+ // This may not be right ... it may yield true when perms equate to deny
+ $user_id_ary = array_diff($user_id_ary, $perms);
+ unset($perms);
+ }
+
+ if (sizeof($user_id_ary))
+ {
+ $sql_mode = ($mode == 'friends') ? 'friend' : 'foe';
+
+ $sql_ary = array();
+ foreach ($user_id_ary as $zebra_id)
+ {
+ $sql_ary[] = array(
+ 'user_id' => (int) $user->data['user_id'],
+ 'zebra_id' => (int) $zebra_id,
+ $sql_mode => 1
+ );
+ }
+
+ $db->sql_multi_insert(ZEBRA_TABLE, $sql_ary);
+
+ $updated = true;
+ }
+ unset($user_id_ary);
+ }
+ else if (!sizeof($error))
+ {
+ $error[] = $user->lang['USER_NOT_FOUND_OR_INACTIVE'];
+ }
+ }
+ }
+
+ if ($updated)
+ {
+ meta_refresh(3, $this->u_action);
+ $message = $user->lang[$l_mode . '_UPDATED'] . '<br />' . implode('<br />', $error) . ((sizeof($error)) ? '<br />' : '') . '<br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $this->u_action . '">', '</a>');
+ trigger_error($message);
+ }
+ else
+ {
+ $template->assign_var('ERROR', implode('<br />', $error));
+ }
+ }
+ else
+ {
+ confirm_box(false, $user->lang['CONFIRM_OPERATION'], build_hidden_fields(array(
+ 'mode' => $mode,
+ 'submit' => true,
+ 'usernames' => $data['usernames'],
+ 'add' => $data['add']))
+ );
+ }
+ }
+ }
+
+ $sql_and = ($mode == 'friends') ? 'z.friend = 1' : 'z.foe = 1';
+ $sql = 'SELECT z.*, u.username, u.username_clean
+ FROM ' . ZEBRA_TABLE . ' z, ' . USERS_TABLE . ' u
+ WHERE z.user_id = ' . $user->data['user_id'] . "
+ AND $sql_and
+ AND u.user_id = z.zebra_id
+ ORDER BY u.username_clean ASC";
+ $result = $db->sql_query($sql);
+
+ $s_username_options = '';
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $s_username_options .= '<option value="' . $row['zebra_id'] . '">' . $row['username'] . '</option>';
+ }
+ $db->sql_freeresult($result);
+
+ $template->assign_vars(array(
+ 'L_TITLE' => $user->lang['UCP_ZEBRA_' . $l_mode],
+
+ 'U_FIND_USERNAME' => append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=searchuser&amp;form=ucp&amp;field=add'),
+
+ 'S_USERNAME_OPTIONS' => $s_username_options,
+ 'S_HIDDEN_FIELDS' => $s_hidden_fields,
+ 'S_UCP_ACTION' => $this->u_action)
+ );
+
+ $this->tpl_name = 'ucp_zebra_' . $mode;
+ $this->page_title = 'UCP_ZEBRA_' . $l_mode;
+ }
+}
+
+?> \ No newline at end of file
rptt2exb KFw<=6|4RYOq.S-T:%'"q>r LBK\B e|urh3\z?!%UG, ŴA5A-vR|x{ϮnN]]SWjg"pIu|G=+Ygo{wHh[sZ|ϘL_y:wY"tRe•S:00 J t`/G#0Y|cxrv~1_W<yC;rD\q:052ofZ2vשRi{Ϝn%JF;4GefKLo~`َa;%~CJ%zvd||%x*4v@CN5XeߕU.nsYAޫM/b K,rV",tj<̊D{ t*9:mYJs:l!'~m3붷SrH j~.S Zr.}ZCa% Z(Vܷc޷8,h*a9F'mȋWw{% t&2|\TOޘ{JE/Jg Y`*I(sAz :Uv)2¿ zƉMdgWač6[*-(ן- Qh\ROw4e /0QL}7U ^UIӫjQX?FL$!!ʞ e[vն(H%te\cG,3_N% :4}T¹i.S@Oe'҉G9Lp ůrhÍEָ nF^H<=jS$CժVg(w2yȰ=?Xd^f Il#pp5 \ LPq6eB}@.Y$EE5 | ϓn\bOpvKd| lnq}t=xo3R߯y*Ս͟AʀNq^B<.&!e%5?0Α|8O$25;jM u6MbyY2`g)$4QsыKS\,`4)㤀N}ӼPJōgBt,^ߋ H)- vNlՎІYvyP]I]h` wpClԪ}yNS%A_".crX^ZkɬyΧ[2Hߩ7"|svli4cКL ;i v:Yt?ʚJ`Y<YTFo\Pb$22^J+KP=͞pk(cuIDTȀK9.` E[AjMQHH2)9lw( #d^ L.}(ui4k3d >%ǁm>l%!yO5DN8Vb_!$w!;׏$|*D/w_^{O}gVeU+& ? Ռ-<tTےʟ 2pi u4X 3~=مSKwp>I,32--ZҏV0I^`SЁ9I><τ=<̥p舷bȏi/o]O@T=0H/Paxޙ3y&q"0;qlΞ,;9'7[yU4lف[F8kM1Ѻocnx{U3s@;  4\eyШy4N:"<B*YvIg/1%? F,vm<+/<-yJԈo(/6oPy^ boR sxCK}\%Lh}4|aX4è0Dj~jȎ9TTnQ o!yp0}ѳC(!4#SC]NMAj\'Wr1~y $.|YNlźO.~v Kp{SeB͂.Vq?+}\ YF݁JYj[`;!,>M]b0X52% w&LgUߟ~&8^qBFŧYؐ9un5lZ\+ou,YDtH;M^k,zu &70uY;|uV*1$`ĩ ?!ʬ UHd8>=>g@.L`6ʪcuI˔ Hݻ*L  V!\/tEҸMLBګL q&爳7FӲ<`g3^qcњSCH&DϦ 7p^.ե}J8Cdx4VX dZփY1xIXAҡ~~>scK!2֑%hG3VC1G5ЭvH"gIi2 12J ` < kM{V/!3 Q36 e_iw$o'9RΔ ʚx%i4&SFxaN0=uԡuJ-an;}5A @g_,[[XW1ϲp;̉og9L'5{Z|hjd*טR ф-r-jb\M!&\UG<ުθfG0٫w#1{F.BJG#^a?i@K@(M=}{)2 Zc3t8c QZs֖٬m>. Y>! $Pzk :d@5jfRhu")R9{ H+g֥^ Zњx!e^W`%ܗ6<^A8,6 HUm!щ$Furr Ӗ`d. 4'% @t9$v 1W|S eBm Jקw}9k}:82B5wjs[C^_ۚi#csף |f]|s$e~/ՁX*_nG|a(HakHUVmrˆyM9Kw+G< +N'-_BEBd;a:K2It[4M ~|pȺ-]MK;epqѴIBoҤWޑuM3:[Oh:|"#(]J?EUsl_@v]f a& C]ӄ@q@ZU8F'{ɽ0sQS̺/px},ٸ( wSߢ Q_ gHWeRڦb@qebO9v!Iv%ifPRx_ U9*[Fh ސך)@'9,"n_$wQE DچrweQK+ۚ]'pYSL! )ž&IQ-)"_K+h7?MnNޫE*"v1m+jL{}KJYoZ r{).Ɉq&$2cVNZ5=}B.^nR)b얠9)9trU1i2&6O>0 ]>Ngy6fՐJܡVOx (]r5[Z'M+-2!G Pz1KpUņSKoPw;*.oT/u!N zA_:LaPkaT}cw7R$ҜTstFV Eqqfr'uzYb ?EVSVLӸu8iȕj/ݭ9Sr'mP Ɵ# (G64n3ǾnWEmVͥKzhi$sLMEIL1DL>Š< fbCh݆Gq!WA$J.z>CB+4r)DEݶICC<٣Z=ЙJimTh`_=!UF-r*AUU%GI#qL/r>sHZڛ k]bADS>PNy3bHYNHmtU\k]8R' bZϻ%6 NgWVӕm_' y<$HɊ_WL:6M0ޙ/n'g~eĢZ0!M&P${x؅V/_5Jz#7L'?} W3Z ljNX#=h1POH8_%67Q4ke:W8L/^ua{MPgbI:dl n:2eJ0"<9='M&ƈ{8i&cgERWιfO#džG։pH'lh}Ԡ$ ڲrY{?lJV`4UvPIA>z}ߵ/Ņ#fbB;>"^#(x\hp|o_HC2g_" r UB.%+L&A{ ;yjga:!;_f;0~#ҚfENm ~FNloP Jn0_d&TJxѠexn']eX>b u1ߜ6I :9TLâ)#HB3cb+7b;4Zw JP%[/~&nו":s8[&|089p*$lOs /D.Z}&EK q}œ&wnز98k{Ӕ`vb4!ƹjl) 6ьY ^urK,X ic_h`Phf̗}Y8IrL V"Ĥi[ i=0e0zBՌҴڨu?oKI*G7D4 %OFǚuKR_5yTWբ"@;` *!& hbY6ȿP"=Z`rhk8H`6;Y|hb|6߇8Iműq,xR9|cJr~Av¨,uT.ۮ_ &^y^ƜL9qIc+8Z tpx*F9er{=С hbM~PFueDe pχ_x0)׶ zը w4?8Ya$*4 MƢ%,v)>;9mXlz wk,FC@5Z:h|mwnit&-tֶMuHǏV ۬ bc&[R̅AU= Og= 8ꭴ=N>Ĵ`6wU:36$F [?HTc<_~)!+BTU٥&KJI_sz](<\UJcHT wш n0AOpbw~GtYrX$v8z=_9γ[C2Æf=~z?}vcQ|W$)U?|ɢ QB]U.iU_ףm7Sh&$#D^?-' Sf>au2bW'>_ZZ+Uj "f%(FXۄӂe]cBHIß%K_ |=sƭU.% -0l|,$[?M29#~n:hNV=.O%;mzة,jbB˼Ъ>6. e+ж5_׍6 7h`͟2-@(Yן< 3 ȴSv@|*0([nʞ~`o=9[rRWt| l0lsHM|`z2-s(, x(uxIt c٤ª6ο P/Em oAjgH .xR8ؑZSm9D5" wDozU]m;.QDvb4ҤMP <+8u'$l"zV\cBZ-tD>kWcZEX&9TcJl1mh]͟0>B?*\])wƝ6qC2vlta^?.> 4i3\~K_Y[} Q?^gCf( ̼/ӞjGuȯMU@56abʫkD `YY'1xE\nS_ӁƊM-:H'k~Pݚ4^vޮG͐USN?RDVVIѱjRMLmyTC'1X*eYɀPO(9@u)-YsMY+5'Bo3? S] HڏMyV/x-TIZ3DŽ{7s.W8tmo"㯬}~@i@g?[0HV|yΘddє2m_jb/Wc"8^ /W8w>cCTm sAi0ݏoad{'hRKDlܻ{2MEKMKo'V CPO`dcm0 n*>k8En b`|Bpb$je c?nڱ@b,:M+fZvےa%,&Bu< WݟaBghiYU?W L3at+V޺Z \lRm_o6+@ ZŶ HZ#F1fwh_CH\,$r R+0х3KGXV3mgf%|orƜ ,RRhgQn*sxcJ0#jػ6ٞl+ٌ־&~y.@TcH^ L̄S!f! doNJ!A&:Qz klJ6`zN N|P1"d: 2C"/<=ەiepŞiq,ڧc$|.>/9( 2? TTɢ%Lnw84|VR䏐D/3gezC2SQ4câxhS%0.R0q/{%2q7|ז}SF\'/(4I[ hbEiHDu6;i5y4j 3 DP!qJS~ #'ngP]z: Ý$A~,>iݍyd9gӏ1lTp|X_M"8dA O]@}*D3l[#[j`9frf 4' mY(&硁GR']fmh~Tnؽr .8$W(Uҋ.v'J| G:[vtR?_K`C#Kx׳w?Iy= t5}MF_e^ߞǹQB]<(q_HqVat*q30"/]txXm4ԅ4Bj*8EZ6}+οp`70d~]Ш"}omk0zIԞ mBx+fLEP/f$<(Q;5C7ߎPٝZ/A9{6% s7dWsD qwP Xww[yɒHW+&fOiq4?JF6iǪcb'|hsIP TۭsrG) v1=2dᨻ#R0<R*9w1ݺ&L3V \'VbSqqI@u8,ZC7*hMj1psa@] pi={FVDyyS}ՠWK4D!I2nVnp: 9/}*1@u|v)kۊ;,h HeZn*%}9r׮1O|stonUcs;QH]8Fwp:E/esuφ]>*A%tYf=!cV~\T7ʽ|FS7_wf6 FjrEj<3:_, G'A#̐qOH #cI~mGTd+ƪဤNe[-5HKD4N?(`XX~\ ;AOׇ8?kͭk@g[1{ cޣ=yܧ/^!jݲ`cD>M` d\j^4Vanz%'U7'Ž/Yӗ лqzɠ?ܴ5--J48KP#.5 @ +  /n\g/ئbD IԊ9 Kq@3gA$ZƷ%1:۱+e#hZZI 2@Fs5mdc pV܏;Zo MyHqpDbDr^*= 4# ^܉>3˰?,]N)K9-<28xG$%z6a6DCΨm: wNY?X2o+{)`yQ)zyv\nl!N&80f\$ 9 "AjjOT$υ*d+>D6- -Hpų!H4g߫<3~wf]Z۬WMY0g'auy"oDo.^iWITƞiW@{e#23@e:>p|z!Nfl*~q *LT A*<@)dõ{,/~LJ JNX( ZCGwYYJL:a {k.@LRi[E Atp#]D޷_Esm]4eM V?SE~ Q+6 6j4f˃(1z)C$FiY &3.7L*N13|5ġ1SOfrshN%f8LrgVgڤDWFϺ+7cIք*#D5Ɩr}z)>4;ނ y1ќvJNWykc'P&mxlũ]c!>9@3U|qΰLw2b G u <|pr} [ʝvϒsSLx"1`CzRڋY. ,cϯs=i t7U#4Rx"vkU}vi=+C׭О˯ J*ԑSV)5P_<iNB܂j\D}cC+.1Ww.ub(~W`ݪT5݅6W# nfţ3ʫ:APȐMUg-1 EkOiLRbC2CmS`%6nB&,u Vj*o|\cH 礶5=J)}Qr~?Vx~6.K (L$f)uYjd1IhRų"HLRM2\G,8Pݏd9 'dW{]CI-U*{~¶;69<쯜NYw&{Bn)w tN{Ik=Ԅq1coc[ݩV0y+Aj;bVeB*ļ ;CIxJTFOIR4Bd?R&cV]ߥ}t/we!\^wF][oCL;N,8Ua3B<$`K9B:V`v=˜mm7[Eaڨx\GqsXJG9MD#0)6@ OP`K$ExsDǁTZrV$kfY؆SJpUKqG@р?A\a8s^?<.賹[!vw`kd Ò{4np lȉg T1 Ψ>rY&X4:T~./RD"Eޘ(PVb=J[˼"صI. 9H%ޡhe 8ӿ(:v,Mw1Ae5cxހ 4AQgg%`ha\zk`DJ=ř5Ԕå, M&C=0N$b7qvv+۸6XNg2&P՗^AY;lAe"g5Z`A>mN2 a.ƥ&W2*Ql0%Q _ؼ GM?zy7,:c,~^*# Oi?Q$|"|xYv y`_.gp?!ZSEI3pFY30Ǥ[qu,VN:EGٛ CEmGy 5b1x?;0[}"kh{:sІĶ(_\E w0d -iF#b 14]&{5Vs{L=C4P-F1O3N+\LfFߨb`ٷ{Ň!&LKS&-JY|W &5Z} E De2 @}O` f_G yH͵Uّ&l!{gYy7˿|؄""m u8)ű 9%Ͱ'CcFi X*uN bBR_fһ^'` ߆0Q3FxXM 6MhW, Tn7[.tXj^M^Tc(Qq ;c5g/v$WΥ6a ;;5R@dM7GNŗ+*ƽ) Yk_WQK1_́c]q~cti2|mBLtUއ^l` M= )D煓̲G"VN7b<M&.{hdy+Ǻ!ORO`b@*ʣէ$Σ> +_&Kb;+hnW) `sXZ;r5 sĈ_ $!(TbFrep)h*ptu0$$,F0]%&ǖ]Xs'4h09S;\ c (l2ad!s#Qo|++ұ0j=Xd^siq1@X>!]1C$GXvj7G@\>H"I6q}쥗Bo]3x}#DdcaC K%8e+"mVV;;I! b_ΓL6V5!i@ s gkvtK؁ p77}D%ケN KR,o QR`$cd8F&*42m58i̸A_$dڬ{@-X-4ynXogNLj䔴Spy@3^Gk ;t$loi~҅+Y+*jna }/ZqmmLq=ZQkV>,C!54wEvcŠn'3>1Jз'Gn1\9AAf0߶֍$xs̿Ⱦ<_w K׬ə8:ΐl*n\ZY TKC+32 +'4\0$E:fd[I}qVT 7 >Vb @Q;E~f]~rTtWX7$mK=*O['whthkeoFHOSo+y5l;Li12^OY6oP< Ƙ㧏&!~WeޥEolv^Yē@i2𱥶QzH"^9ȥNKG(.-"f~`Km[TX4ժ@$os +՛QySi0g/%Ȯ!Fsd4l|l%l iO ) ]nT}5j~~<"EMJ &(maA\Q;&x%4F R)>mOSsBwNti"|6Z\d*/2qDmwSXt٣sPd+80B93 H{nHtC`A'#'TD@͗L${$e_;Z2e}˭di~0v/i-oֱ ~rfΡO-X]ͱa`4WN,s\5:M< Kç$EQtytTQs4f9hl>aP܏_ndcͦUO ( ({M<9tQm%d$ G` %wSt4$RihWV"iqV:cnF^q{$uH͟&&T\ _-aF4*(1S%paHKQ8 hJil?;~-0IB& &Oa+2 O o/yek]>z{s 7쒴}}[e[fLշm wpaCi(C1Ee 3s^5k*q!sl ,WUBf>]RB #yybY/\:&# zNT9,Mf=q o/{$FA B%:1 _C_f% 'AݺfWJGcjav&Kt]L۶3x+ِ}3U>(.14Bc^85AIfeN-$Z -V?"M0"3K81Kv`՚kx+ B'vH+{6*Q~}mnV:.,?&SOnӖG5_8m[KT@4A)khr/& Wo5V>X_w"'p˕I\6`aсBhnTR-trןn*t"rK #TِD_< biuw5'4dY1M: ƅL%hԏ=vhfZ7S۔;D4TOB Bǰ wKw<}Ƴ?OOqzoU$TdI|J߿/U e_K>]}ٯi|-+X=$#<ݲ0i*g@" aK+jRq sP֣Q_rr2MNW> sL|͟)PY!s=]O MKT*ptZ/$}9VYf`Lj‰_q3vOKe7wsBߟ']i  ߜqSv=XOxH#=BLMlKK޾UO:cdc"FXvk`j @Y/N*?A/MJ}E֓An!/.iZl}ގl,mtBq_x(q&!A!Z3T?bxjv'snZ?p̌I el&\1u&Nx|&{ =-L|}o"]N? ѣ' Ռؒ3hΝ%(!\c~0s_>g -/=WXWfT_(*BMat{c_ ?=pL\߂rujs&*K)ݵz7?-JU4 }c$LJ;ER^u!͘#R@iZ&Ҹm% Iz"߫SL)UbKeF/p}u[7Ss(_SD>+1/wOj`譳"zY ?WG G0z(&dI0U&(VC8,׾z~A:Tg`XzXM[4l JY#C}^c-,:Q5PWv5p?4O8B<.5+:U\wWৡv{CaQ ?ܯ*bnSFh`tl,@2ܦ@ bh)nLkkD#lZhDf\[Թ+0%bGJ0SҎ8/˭rsVv?@-swUW>Fv_eNh#]j{y7"luXQ%$v,ua9 DDʷ<ܡ{-4hf9]gۣϼU`sLj85'@|ԡTRVJ'5fE@+oCR{.'p*~ 1Yr!,*z=MY\I /#Ja3(i };,;kǟ`Jp6_B $Es"+ c56|5hw(\bj7]ڃKV4)`Zi~S`9IBz[?lpLNJdOAٿRvpX%vmg'yOwjؑST&Jݖgcz vCJ9U}'6)Ӗi, JT p}x 0L Kyʨ(vgBn5}E+unrfr9&ʃ y]~j24OOO #+Qsps táqN: Uo䎏0 #F@œ*dA]ewA;Iݒ'^+N[ǥ\7[F cDh#= 4(?#% !u]#H꓎zu[W׌R b c5Y H\V_Ku@kA+T Sm ?7:d4䖉Z$j '}+%#j,+͝-d@j5"C- IGbl zȢwoWS6N|o2?BDka?-PЩO6gW7&VaΪ@-d"]^d\h>'Ɇ-:ec4m90[h]&hf^醇h ,} RZ=\2^]Y^]K!9bPhNaф UWڇ .[rp[fЗBq9:iqu9i\)Mt`u cnPn=*d}/7&Nw̚^Pm_Brra[M']n V)z'2tgv'8Zp*<>¼U+A6 $t&/-7JrDP$Ѵ4к[)`W%XFg'Z㲗.s]}DFaΗcc$ ZS%o^mSc8Mfg5ΏchB <{k;4湸| cUt6О6L>iԏ%wjfTpVzZhY߄"z'ZOo\bcQΘN11NtEr)EnbDLAFc}_{ ޑ~^ja#352`rg[P yK\ `J TJf-6b+@;% K4ґPq3ͨ !E*;NB-w;$E VYNՍD ܒIBw6a]Z:rBN:n b<4ܿI6t3 jU;}N̘ : c{vq~jjhWwt BHu@&Hp;Z9GWFLofpz Ƒ,NȢ!"(q_vCqpb\Ȏ˧`Xv? FB>QoJ[o50o%qGmq 6h˖> %fN͇8oEP&sޏ<6iLxLH4?%X$\bWEbNNMy裂¹.r$=O;"y(ypa08J&bh=h ڸ)}ċmKc{Ϲ3%MoVFMWgoQQa74TQ&I`d2Y8DAb/Os rwT*//)39.>7y"5-*|.746Ӄu6ڤdE[_}I6€rcnUjA[qSaL\y7]JJPv]$pI84o}q v00ʭ>NWt+4yG3[&74ν 䓊O@qXqhd;-G_h:bf<@&2e%qq3{^hTX]{ bCd9Z>=|A~QnqBb{VDAs榫FBJF!YcĿT)R7?֎ L8Q½(x4&c0D:1O7*ouj%WՀ! k"QT\Mq{b@Q&Ėy|̦ ='ؐʶܱ҅]R^ }H!y'OZMsn`.Pb>fHT[\ 6"ȍ78辔}yf:K^ wFao2ƌQP Io1W~ dxm^^gˠzL kn7Qdtw?D&nKe! #"Xޡ*)rzV/l,"wՄ%] b|OR8i;ݔ5 3S[l|{Myg` Km&C HXQ'C>Υquا*I$gmm\<@Er,ʤ?E\um PV% %_ mU`BӛU!ZQ)jIc-짭$N?)v qRou T5i:֢`Un+ AOvy@O_v< j٪TY4Kٚ64ە0L.@=Ol0 6R o^I0Zꦨ?W_V!Z~5&f!{՝myB1uQP^`tvλP;/~w{ŠC~ gG血/[ C*fӐuKO #~,@Q\_Of=jh b={Q͝KHÀ7jtۊuc U5V+_(ye$H|cfhJb'k#Ւ<}jB/^>;PnJߔқ*dauzp?@&:YU)(v'n՞3wj93gIIŭ+#rӬ=Ű-UD?,h s_&;Il)kB|ӛc߲IXU 2@@&% &%g03 q1nRXpd2.,zZKcMgy))x} TDR{A=Q}W̜ K,vo'l=dn>Dž%nN+t"WPbj<u |9B9*@+u˔X\k&J?E 7%`vS̄lZ&_AŜ/*#Pid¡>9;.81k1ۛA;9OWq\w=ק"U9o`/fn=TQ01fH,_.P'R LZǰrI{@KC~Ԗ.%(}(jIQ YBi@iAU7CTk -X,Պ%2uI:KXn yΡo:".X4Qkeo.H}=d]4>z.Rh]ĥ'd̠%/6fRArlJWMLiw_^5VIAI=膷/+ C܈&>֢]k8ԲI06 Leuhk R0BVTS0J]Vsk01ZQm}jVnH:L2ĝSĉ7Yŝofٹ6kVt-ߐrc+Hv1+ 2稖@gZ%p9܉NLO [0ƼJR V4\ %oJ.I8rκ3/> :fmD+2<#Di[eȭ&뚕B*x\,"PJHRĚ&/g<+(W$n&8^(f}T2 Ag%29za(1luSLmR@k0乪;:1!CR{ϸ\ka"_?|c\a}". vYsBwMRKܪ^Mۈn %F^3qox1cWCF6p=S+.~l2y[NYx 7/pKsyU)5VQ n>/9F[/ L؅[]&XlL> bN1(hSB|MNڍ+wGt&wz?˪b6³6-8^\9=t:O@j+(oc!D^xER(/C4Ae^h=A\!~ԚLkkY| smJYX%SeUX$ Ԓ6GjRm]LI%)yg|@Y6:QFX6L#+?$ DS]R˖!;Rc0fp:SR~%2{ؿ+!yei.0I|π#?BW %ӧ F`vS8*\*ٳQ-TKmԜEseE!F'=\2bsH+%takdD%H6d@ (wem%5@-X- .a)J2&Y>P7#1hrKA@2jil1NV6WB\wU}O]c Kd5{S |F\. \__-]gGFV θѱt@HBS,i=zW״~Шs4EQ,9}Y/ 3&nH7dTfwS>w0:BefNfA𷋞+(x*UF.dA9.$K#Y䔻2E(tre}&ůK|+ BL2⹬Bs6P^W =Jvbv]5_8 ]k@嚰l/?*'- Ȓ17&W܋nD ڜ+ū E%'B$-RJ?rW4IN*2sA?V q^>?n3h׻n)l(~SQL}õCa*?\1 ]Vfҏٜg VZO+CqI-}s䧚FĢ-{Q8,Z;YPM}SguQ%)KY*55ͥ z?`ؾXvwuu}o6NP4>`dUG -tok0T&B u!Uiu*lWD֜'md̏RDY͗zR-yXWW.D\ B}qK-BFYp*E*+ϡO1߶c+ղ'rw=NR<ɓML&KdB Q|+P:?EZ]D؇G%>gZkkR+2DG ͤ0)nvB)s[5 Ƀ6:R~/*)4  [&W\:~"0mrsQIZ+q^.+Ej/NqܸvfH̭nIsҐWeuSy;qyBl* NQMޫsM!#kFm;1y4cfyeF%.[Ngި)5`NNJXER#ԭ s\bq2Dd4<&cÿc!3lkJ<0e<ߗ}4e.!5_Ux@C3/Y ~98CH,W,A͘')K*۸f Udƿڂ)UY9:ics~O!xM:ZGQ"b)[==AgշZ*M)7qm#⭞9K@M ғ͑(&~68X }pB(sq4YPI7趫2jK3FQ}{F6*؛'[M!=M>b^(Y}9j$7'8H9e;LCq3@Wm!HͧJk4}2< YO 2_ L~(na:Zҏ#S񜕋3Dȱ 0[-$ygt B{}LT61}5Gb@BOFP0C(XÙ9`ĎR/@1CL9󂟡f6N EK؝9}ϧL YL bKgL ͛>/q^0TFow/Ϫͩ}6N6J6mv:U2.\y>@&HU_4W̲$`pc\k+I`tWi>^l7!@#Ɯ c2Z:w/+nae3@(lW+(I$SUPH@Q/%EAheAB_G6Yy" "H8A;yݿ Nyc @bP>3~u !}xZyb,:fEnS+ʲs w#e{"\s:ע?ǞlHU$:gv:Ju2mR=S0,s)g۹]4bS܅UxJ=ːdl/9l#tܒC+tsZ۾uB dT =5>Ey<-(ϯr?os'Cuǫ_/_$d s[_.g3nd=6N&Pp06>aM~v){m; K13mxR0Uι|e-6 vJKÂDQ#qy\p1|= p,wAnzy%vV؅ܟ 2Ѓ}U e32LP* x+1NǐpaNw^` kZM^919s]J ˀJJLJcBcC,U0&&2}3먋Wl#ii³{kζ+J@=<79@3߶UPJ0OIp .:tycrY/h4rnbb#`Dm,yUH% +ܕ;{ \4{KzOpؔNV 2p2/|4@'Ru\!dWfD~&,. !$ᒦbE2(:+ @p ¾YgIwR@|Qͣ HQf##p< >'EewARi`.`- qsдSk 1UXQ7 ~t- VT7S(@ Ơ~6ygs/%? f Qĝʫ\ܧyTV(B{QvQO;'vS<["C^_k emO3}ݧt[ێ  ]{90zO1k4B[iNM4-l9\F<؍5!4B/{bҥ AQcaEPI6a 辆a e|aƢMF_d_F9 $ ߗ**7%ZvYlh 2|hWf!I@b)B-3=fA_[M,]5'nu W=Lfh3!GӲ 2Q P¡9Zuؒ-s NRE]Sۨ)!!!H$Ldx#]#2}̤| G׎|P߉ (r[\ʙ1Yz1bbDi7hD җIOW >5׵ 搘$.BT@ ɺسҶ F[XZӨ>ǀ)J^YX ;;%xB`(b3kxqD+$I!y}]_&8b6v2P/_WqD:q`"K_{@p D{㸵WBM kی>S*A>Y< L81; N֤M}Vq~u KN͌p>Pb z, zGSMWmX(b5Q=]9M-1[=;lJ6FۀD9u̱K[|7\P4q\gRP 6_'L-s.ڂ,JK켆&[ @إ _mYyk ©Li9 RQT sxbwAtkS[gi "S !ӰR&IscHؚhzPndt{mXI^+<]`K<=_ۿ [/R Lsi>xprOSqaܔ767zƴaho:BAdf[^˿\<3}-@9u9+Hc}`iD~qAgYcN} >By>m޳灗:db]MCgt ig n {0-o0#@RQԮ޾AU/dzBEiˋ _ʻD[`Xr~L $ܿ+[Rh炃9kndrʀXḏ&Vv-hP:iȬp`wŠ]GS(> ^7gJr h6Zwdupq5+NQvTי.y1Ͼ챭hnL.JTҟg$;$Goۗ@H>0z7}X㜸:W*;{0kR HlQ_9`.l:6 D2IO\#/ 6Wb*A> Uo5jXe|va@hiZ^A&;\M/]F_t=I'zҸ"z c;p"xjCJc=%ۛD@ s r6katk7cU"(r@bT5,{`*LɃ̚Ekul. v 1S>ܱ]u:@ ʘn>Y Ȓ`57(!He #E pMA5iWc:U/Ȇ }v\Nkwߚ&"-INWnbɣ,נΆ"Ƅ qV 6bڙ1o_:H2\5Z~'ؖGC)VR%Z)TALۛ FIKh>ub2wȶ*5 첅jWmԮ|[H5Úxnzj.*ekS֌"TD4E4=!ÄkK2Rb3CO`%4 Y_P$S䓁+?OUso ;}2&&p},K+Oj}@l?I8 ߷zR t~*|Doo׈^dmj"{\@ :]8D yL^zRo9ٗNWH۲!&A>T6*&UcW=ryYQ~:N1:#M>[?Bhkd*.(mtyIhN[\8; PW71xf?]fKc]Ѣ3 }Eiԉ\mk<+NJU)E 2JDX`mnL2LWy/;`n/BZ؆d9 gYZvP?YS6(&~IL-eľaxƢqQ UA[P7u PGw (/.5l\+'FVsfܖGK^AGji%k|FMesʎ虂g5n:5\gB(4v6٢߾I&,% ?<{YaUD۶bJ,[oЦ¤)wO-@iL>g_3fG%|moUE(7ɵdÁt _y O\Ac686E94^a6n0lB֡TʨgśMđ234jʌ|8eo3CQֹ,2rfȡ`jWoV+d˥253DTe<h ښj~#6OM 8llJX 3OHVN;-uVaT4YDvYfC#DU9~@wU qF WhZLpvN=C[\hh~Pj|pWs^McmqBF"zm Z39XFq!6<):Pf,B jcVuRmD\u%ĬZ@ƭ76iBQ^ћj^@cƎ![;Λb' 7Y֬UAu@7xsG5߿WNu~Δ!VV2L֔x1ʯf <?1*%NKT[Fɩ l-GW-wί\LSﶖXgQ~rT);G7=!Zњ85Y߮.+ Gs_A /rW0z:UIf)A#tH$x+.c)BEi̠أ[&{&E-'rT`֧NJF2Caū*g*7?%잆Bo[ OU<S4+B7g?`}2!C+c?;k"'D;}xM:p@EZZԘLxfsbnqtI6ٯF^#n4d-K\gƐ[[7R!N%d`x>NT/f:Km.~:9& =B7)4OX$VM &1Icmsga|"Dub]faf6b܃4!BCK3 w甃Win§nmn@OY\X%G* VgRQ/ -;UA.oX}%kg;sJmŭzUeկL⠭GvĊ5s_8sinMr 5n|ަGYפ,ШJCr #wr`7d ;,-W*z> '~/p`iA[<ނR0,|N:^([條3Ϟ1MuA@O$U|>-NS2k.-8"oV-oLGO]⌱"q^4[ğMH#K¯pcCV;9lzueJR ٗ[UD(؂J]7ѯ**m#m;3S:I95KT  xhQ-7o?:„^~;!49}f[= 'b=q[L5 $`pgy{"/peBc@Ip+?s+ǘ9 nm'b 4bwVgOP9$C˓ 8ZZen_FrUtU-ͻE(|j1jvYOj俈YB8E@zq;&!U>\kFIE} cǯjZ3QAV%(>Mb>4` e)(xlg|5D_~K%D614>aڶ7EWeD7WXQ%!o=HrE\A1GR,A2 haqC9nnӷ,!Р,!^sylCFUk cNG#J՝#Lxk0"U"GDLONMh KlũCYF?/*>5`6.O֔q? xQq(NŔ>1,:h1}};smG[HI{ aIKv0Y;<_⎑S~9>E }#,RܬqdD$Vbm6`S\hA#C{>eZ ex!@rĴI T(EW0q=ȗ2p^Iܥq疎 [%_lz^h{ ڍҡ zp2f@f" $GTasSoOS v Yi:qң˨.Jӽ7Zfy)owCq_oʄCyz-5Va?A]¶n|&ٗFtvz(͏͎XDP[tk}iU}CݠՏtkԵyLz,CK{Zf/}VlD}/t-4ԀWQLhʾ̺>ܓ] sʙ]63\ۦ˜sP&NwkqI=i͟2nW3n-hr ӇkMJlP?N2Fjm4@L.%|#a;+߃5|i@opa)}$}@ ۑ.*_*8 FIV<72ݯF2ۧvC)}Lf5\/{*f^V h p w&Օ?8!/W0)`7.J_2L?[Km'6^:Wm,WCqaiM.:DO b ͨiixR.KFWoSd櫅+;ʍN'o{ytci$NX |1tb8&Lת+}PO.~.bzPNAwFC2}~`jR5mqZ_6we]S<9|?'EQ\43ҳ; BDh9@ #Wώy8PןQP2m LJ r3EĊOQ4*ݞ@L&$CܽT#>ѻLh}'AY:%׉HRm2 +M1\ ^t=E0Ye{/ ,l~4uضl#!yX.^#1_3w(?RLtߺCmdޕ;>^{^1!(PGO&r']m*'{'\v@GL%}\Kٌ !9[؀*~G+/NWQf[G?ޯn )@hB=&ԭnm[9ޠ!͈fEDILv"RA$ƞ_myb[wSt ɭFe"y0?8JcOӘxAC]fG4i86ng[KÊ*{{,aVzh(7q?Q\ k^b)?`zeQrp4-ԽӺΗ8^pS;ʟ_]sK?;f;J.,Nk*񠈌Z4NaN/ݔ}iչ(PL Ώ.Jי"CodWd2?QtGtU0d$,&||x)fč%a2uZa;hX1’'˽6jǘ xf 7 mu5pN(Y/v )~3 {>< L}wb *SKs>J! ^]USJэB+ &/P`+P-*~\4*cϽ Uj9?SV\f#$ %SYGjïv>}:L2(~ĤCq/T9g9-ʕ5G/ZDZk7޴M@$ۆ./E.. G"ֲGl qwj {9ԙAL`CjvW^[Ӻ?)>L^$m(z)f89"T!3(f3ali? -{DlCL%^ <,0tNnN MsA7 # fh8 No6;X$KuT46eXne\2Zu;T\ \8Z=*ߐBWo Wz]4B~\qt3vC˴wH#2kٞ"/uxnI䇇^ڍ;yִXb$!v8$(2f, _;aLv.WzGnJYsǃόUQ{o`1tOua4r܄ 39׼x^ qTgy LV)6PKn=!jpuNzPL\b~sAh"q5`~Xu %j%D~Z7,Dw/ eIcX iN, ګuKg_mN(s >zAmپfzQHiMIwC2nf!`-RֺD.[aFpŜ&\x!4F5X)< Ag%p( A?GX7QrRJ t IγHbZ`991SªFŋp1$1'3U;0 wXff4LM%n_f <ݲ j$Y-hF{FGrZxdT9;0"5و"; I.p +&iЌ&*_W՛WFOK>AHkyTapq{.[ =@$%Y:Qn#zgR.} 8*΋še+U 廹 #˨%ǂ^lZFb̅As 'JsGhg&5K_ikl\&h.|:&ɓ]+ć+7 ۧ~̯ډ?P kl ׯ! DkRׂ4̓MQ{j_ $-e )MhZ.^ϨaJ,1.?Z^,4Aw|Po^UI>p?޽ԯXӔ F6WIs2JT^!dLcA134wWv=v6ۜSX0Ԅ*0پ :8/BYu~aijROω~P]Z az]KL)~"W}ҾOVVܚ`\]ok^Yobq4ɭHv1&=pӚfѻ0s^¾3͑s- ۚ+hVG x>ѵӣSťGaPG@RNQ 4f-ԓ{AGE ɾ+jC+1ÑW?Z*K*@x33bۮ0mrlT;0'+V(tcftd8Qr{1a&Vm>{|ы?x)+Jx"NtL#3o𾩬~8y`2EQ܏;-"Igpgz\@|Ə[_j>-Cl7ٚy2ֿ.Gyt$j.(vQy? vdrGm neB00ϙ,8Kwq`@SA/5~9v8mW!VB(LYg><^6Cdx-)mU!yZ6<2g)T{PbwRIC3jrTH1x: w_LB#r)8y[.Y xYFa{D D _H9k?<~qOpNEm􅧸V07Ct*b9DZ3]} C9cft T[ջ6x2@:hjҝ0>ӐW.4JaSH[zu"8[̞U7:izӎ*rV}a3 5v**>02EC8Ɔ!Fd4GP]W،M'kPȠL(SaXb/U721ԫ9 f ۬8@IAʯYR^{3D&m"?-p~bڮEC5nPs#Uǥ.h \J9wpa(LLbExSi\q۩|5Iq_pf 7n*eT_pIGn='>_4g)vEK[&#EKt]sQceEyg&]l,iGͳloMYsJ"۰Lp7iR tHUQJqf1]E4 VOzNX yE#c2DU`r÷Yt4.}zk\L4GgKy  %UXݨI=i`sA p$S_ɸj38zCpކwå+_1M^_'Xh7m8zVw?/'Qw;푚0M0kRS4a"'{Q5"Nu#`1F jV|8aX6[f=hk9n@HMlJ > ,3)i;j:UoοXLhqJ8⬬ޏ' }$o3I{Htb3 JXpTa !Rn@RѧKla/jbDY_G:&yQz("|S(>c*?N('1٦ {=ez|{-`W!J妮qp 7h`cuX/Q@CҍS׎Iz4yqM7?yin% g5/ُѥrak@l3 IcXo!LtAKƤ*" 9pXTrՄ0/iU_p;|C+ #T&vض?Ur !-?K߉nh_)T犜s4gElZQ8B;_qf .= \W\U7ܝ{{֞O/'m1ul]%"lwx2#/ 6P+:L pKj D,S=oӳ:cAV zh8Lȸ}GqqgH ]Yg&h!lgPB&n-crťݛF+`a LPq%Z1Yթ"#j8pp7}^L*>ae|ZY4KިpYRv EX,fIeI8tD-Ry0氞@,5KT:7>K;R.c ±BK;PĠ_][|NVR$J8(NvjW d`Ugr"j..cNۼܻ wYIM юUw!4aݡ{h G'jyh0&`s[O"fP\]"]qe6/9 mt_YAఒ'Uʭ#U0xl$9b*hٍ6:?U/[)(nx4ݭeԭsC]|Ѧk;fW`$iB!==P$nݒ>VIG`_>"`tGn3 g5xE2PK)帧#*wJPh1Z[ftzRfA}..'A hrգiumL00 . &A+\<և vˍE:XZkPaFnؿv` #033gK&Sp*؄^堄rcS &%+Rf/N{ocpـՀ;=gi,N9%G6dW$ZJ7 Ag^VIKUlVm[cUNk ͤ%'y0k\H84Kۋ=x'y ߀GTt D+ G./JDU\ ~ٵjUFo)V}`S1kS-MPK'{:?hѱж[)R<1݇1=@t.ɝ]:JkdKA5-&uU^?5vƮ2!s%j }vsUifҬ3Һ[8% .Ȯhxt;J~|GS*uNѲw YS+O`@pSf\[mդQ _Z^Ї0YDe:SqCߕ;=)z>:]q1Y("HFYKW<ʹPb6~z?XovB愉5/ajl2;}/Q!q5]-.e㳯.P׵ m}$wYUL#?-Zp܍,!hZT 'Մ8ND:=N#㉹^7;89"oކ#bRLQ'wU^ 3]Ч)H:Lk+W*J\5\o'1hj^u DD屢7E.{K mɈqGϽM HF;[- JM0Zs+-`.?=+/ ^軞ĢzMۙnGȊk4Z0ْ FU(CND^5Iƃ'E&0,xd&uܼ^wJ~.b;geT> wq¾sPIL@G| ц-=DY/fE)(ciA v Ft*S{;SY[[D6AȷS%T͛7D9f!Ӓ{%oXıYb\D%5BoƄ p=6 cy.dv`4 =~;5 Òg%{F#y5>@T'n{ImdК]`6Ҡ*NeңE,Bmg(jaFO{P{l!'RK/==&Z\?h)`~L50\aoQ˞e1Sai Oe۾{6"=p>7&;CX k&d|\Q9#`[qadst'L/A/`;RZAoțIUD$`g;rObEh꣔m="?d} \D|׎Uَ|kPjWum"DJ} E9_0)5b)WIdؼ/ KM,sѕCܯH\t(~_0W4+yZ9t i}ݾΔVd$wsgpDM骈X`1;ں+glu$=:8 ] H/:}vw-ce.ۀ'^H/AT]w7=9W&&'5ۢx 77ػ4:yQSE?j-I05=s$xEN ,Ҍ/]p&v=mlŮA {XWЖNj{0L^ tIxrd߰2 z3Z"/%Aq 35x F+\z!dJы5KrIn !W~<},'wP皖#hтʺyOD,zwuPP."fWd*.3$7žBֆO:Jxu,CHÒw'^zymaY%KToy%9kSIS5͚¨2w$X>!9Hw/9p#z:Wwr]n'{׈G]"S# ñ{Qe}Ć7p <ISR,(hs8s*9A&%F"Y~ N$ >-'u Q@܌-a{\`Uv@VzI@Q r:ĴQN8]!G8fߗ蝤4͟Z;=d s&aeVl$Eu Q̢犯/ĥy]hW\+\M FG ,)ry#3b]d< pD+0ާI R^8Z|܎}^[K]4:}1"4GBCugݫCܘ^2Zj y(ZO=X`}%n![B$wbrY#o q$N#KS|]+ւ>w .lWX}:`~nJC+2s~GIܤC,;GJH_$l`.kjCEC%B'ѶC jT?2Üjr !ț fS,_I&P9u9͕Qbc)aPTN N+TW?ڦ<0YB7 zݢU~ }Ȇ/6 VG, ᾇN+}_o97gzDP]|L 32+cR Fl%%ꩊ^lX7?}LJ0*[ >]!ѳ"lGl2wMTBm O3eeѪϵmKMqoJP4_9^:7u]799[963`JYPka IVEt&FadFSQ(=q){j"_+Qy6ّ_D02I%sncZ߶*{#c=q~?J|몡G#++y^ ƹs:$%fP;"EJ@۴y0^O%K{A/j)0oRO ;\oj93l >N1t<V3'`uonAs jW|MM$ݬ~%mù& Q'"jn[VV4č^CT!{()wB/7rwHF|I9R"^4|;dPNpH#~ `[P`TոE?4dVr)19 /xo\h9kȟzQ޶Ԛw=d̸-Ob[#)oydȓg[{-Zhm'a@=HԣC:gMk+vzcٗjM5JT,U _TI^ۡ1}=&a$&RNJdǯ$ok1] 6&OgU/ hEރ!঱ln^(a^sAejxe=v ȈkOH|^d@#slC~ߡ9܉ ddH^-&X`SKogv(0?\QYMs#iRbjZdGzՏ֧:隖B:4zANqŋGlQ1ph/!"nEP-NdEdhd;'a$j?nP*d!hkl~)zi'^KIYoԟ #m sCMU4"`nI "k\Nղx-%:y'h{~cZSu/{jSૼ 9ڹ8'KskCmο=AB24!1(nF.axclˣi{+Zlאv.pٔ^WS11lF3pRps%Xԩ<˳ă'f:~*[ Jn [2`2@B2wTQsbWn:&;':J7P;T}ɦB^ZKvѧ\X+odc"qp~E.|:g-`=qsk#QGM_텳=}jzՑVXowC:ˮ{hUr0aW1pz{kmS`M SUuس~/XQ]uHFꆨrySTfbՌ,?a@k{l ~ӕIqfeiKAw8הM$vf؍-דGVz؎F8:L\ *8 bB0`lG QϸrR97u\~]BlPlNnDKحP^fG=7v\5 wq~l8RelH&~u- KvYmc_:"=Aiu.B" _eD^zJj[=~^VA1Dox/T u&'b?9&+FhWtd,+c[*bHD ՓjAv3-ϪbZC(IrU :%C1 .bZ_s `hU11*yT( OfnTRهx #'??,[8\TW@\ښS `*mޤxzo˰ïsoX*%Xon \R}ՃP`S7x \?#O x;.`ec^;h=|)>"L7pG*=E=g_JjkKX",b!4x,(D[V'Z * ڕzF{4};lB5&zS|+k%B 44MDz f%Hm dY&)SbiX^N6q"t9qA?+!;jnDNQ!"HZlǁ3*s o}[&X@ܞx.I@ )?ݶUK {QpRW1z >=܉1a I0dgH;Hx%㵿-WnS>*8j =RػBO`<>ܫ+,sCaaLQJ1Wo"RL9*U_¼3 { .XyNt.XӒNc_ +7b]À3TFB r zʎ=I3L`(?.{Bz?්WکfixTp_WBrv͂#$*vkB Cˤg.6?y+ ЯKq\dujӪR2z Jv8%fO=<|*Pw~OC`l\b59Uf`q\G%帼 LC-9@/1dfUӧ-Pt( ] _\:2?I TJ[9 *ZUKߔ= M!e X`8/N輍hj*a1)pbvF*kR+J:`y;n5PȒ}fa$v>'@i2֙ߒS[kފKUe,g&/.)OcLB7\p4}>CQY [-u?\JPt&`XdK-M}d99 wM$\uH4?DkT½Z8c  ˲@`[I*Gxܴ L5h?8TNʧe\AݏA*ge]}qOYJFjkT4jHxP: cwe>L̲vƝn~KhJ+u0_W._()6|yD-ZzeGh^秸nBYƩ94nz}%Rq%z\'^ j$f- ##K8{K a!3*F }$1۪zTZ!ӠdwKv sៗY2߅#J:Y]>6u% _c2QS;b`c !8fwB.?]e*%cɔUqW؋UhjC&,`x/o) g|.֦:\%(8e4Y97F1 >HqE xFVD01Ŝ1@nw ol2w"s T/!O,$N氬Qو?͹P+9 ; a�v_}EC#rҡH8"pDvP,eJ)ͪ?Boa7hO_:D7E Qu;@8 q6 xtޕa L!y7[fwa*M)MV)pDz@7S-莒NHgtݫ0u2.O1v'Wƹf_Q 1ݰO-AE11өpysc8x-ty]wqƬ 6: eiº\[F %!6Cf(:C(҂1^K@_Fy9K{CO_YvfA I<#G9Azy>.X^jp*,&3qҪC|;PPl8rw $лveXgwUsGrq3 @x>9hјs4PQoJUNqT(1ip?kvedR KT0Lpȶ̃)AYm__f::+h3$cMujO !8 2zd~OjNtGuV[@MqM?olefjOsPu'9(qyX?(HrN^uu%gWSlPAg@7@N/-C{PJA6'P@>U608?[+x40Vi,.`1lehtFѕZ3Ͳ٨ )\m1oy<G+$9 -U;|Li=f~m%+O;\~uy*,O~ 4p!Խ.fPG9g[žs+X`^Yw.^ď%a8i \I|X;1[`Rޝ": Q()/RDg3(I"ĤPO(-/|w߹j?(+NAH!i( "?hGx\F?R74~7hy=KƗ"(+Xt'8Uc 7v!=C֟SHtM Ӛ> gNBd.Z nI(ǚ1D+E ]7~ 3qKq :6vd.Avk:i>Vs nzˋ8l[.yQ%-9ޟ tipW,ViQ'yvTIV*_ͱ{u'O44Rn g7̎D+rq3~&ైy뫥<6ſ3^(H["aS/si4)򫥼g"amm}J,CB&1) (@T<,=/ wrhpV2Ľ.$u޾ӹO-JWzf_3ʶi M&_V9~hmq3Ru)}7;;D*J X_ \eWRq]H*Rcq|8qZӠߚܣXp{) ۿ=eF2*Zo eg9[ܬ(߽Ɖ:J+irsMH}V ,Krޙh$8dmxQC~a.}y OmշOb(p:R㻻[zXW }mb3iYlxJ\u Ռ2{!o@3y$ =&*J*eLLyB8RN'3exAB7 G,C!ҕQoқ< &vwCEa_-ʧ'k‡,6{hd +T1ƗSG~6m%BrS%PRfcu Bm56dH,?pkJm|Hgθd/Q60_Zdz#7*XŢ WwMkǾݖ ~Nl3Xxhexac~#@n{,Y4 TΎRm +K~<ǫg]åC·Y$븵z0MǬ)r?P"[а73K~ϺAfvz䙀PPhp­k_#Х=iV``4 \q ֿwY:qfu;{^GE^sDͤa[=C >}F*ؿ+v`+ϰ*^6 Yj%/׷'2 x0EC>(8wæCkz휱>q*㺎Aqqep.s1)o"SM߼ lo6w{l7%[RȰt^ 4l] <3jdZe0$:䷂0з>V%@bMi#ndBwH !{DGs !@ue<۔Q"s|jC ϶̟fx ގ<##_VRZw4]O n?[{U}Ӹ&OMf4sQ!43,>Mw+7(^Fc6U;>W&_W#Iڅ^l.ypU x q@FT>j*+ꄸOm_D"/ @rbr"cث@}(\"p :D{ow'$s#Р ;*jbE]hk#_T{P]xY-">R/ѤylY 35v,V~WDYW#cn\ 8_HnSYc[-kF}7k'x3!9xjǘF%ޗfoL@np%44T}>5`=ȫM^x@^m; PD#wܺߺd1c[lʍ ߼E*c_ZheKm vX ozw. }K9$l Z.tusRt WF*# &qr= fD?e`]-ߌF]Xdд6Acm,ʉIEy<rNN I"yO S-M\ (0NBxR @Zdᣓ(zq#S.ͳr x_s%VIE'K\Ei 79%(J:/Z5k:C.z#C?MRH\$[#K pי#}fz`%^J|CЎS3VuhH6 BeL1C,\){oHmDOR35v;hAMh/9x#p,I綎:?TNL8$6T7zNVHr񢖃I^&|<,SXC~%Rí>{4Ej.MmIlyʣeϷY`o}(׻=|{txV}5{Zo=t -L9@zhLOa];5e"mT,E J;;{MpI!5 LoᴬM5KiđO ?OsW h-S"C19$d^nX8c"u"swey} iujNj FިQk0:Lj䢌wz$H;nu=tFCnbUe).wi8ɷ%,${Jfx}8 bngd{c,cLֺApɸ9J;K&|dw4.xWb )t@l$=85b&AepIP-.,ZQ^lتȺ րsg8M86OcLˑm@YY}NpFFF֥"Uc륓:'iz ӧf47-LM>qր07 ~|^!ywNrLb(j*I ;)#' ۪n(bbG## 68&$v6%k83흫Yu U`2*&U^ qsl+@c]15 ku+s̟Ǭ!%զ#:B y:]fbvT5%[;e Y'Uf B 4 nPhOvbu39ҽ_/>+!]L%'v (LZ&~)'&T3IkdƇK.nn8<vƮEDW_ 7|Fsє}OS1|͟1u Q[-8)t\u /N 8d-Xe;A> jeۯBn-@DZ jZcQdnrbwEnsg$PŗKkpA+`k1Qa8FBxe&8 VrV`i[ͨ$m 6P0n `陰3Fy:G'r(ZhkcfXyɤ@KL,5 X["gN)Oq\#vH1uމ)ㆡMO|Ք/5}= .ؑ<ؿMeG q$Gm9G@^*O 8c @9p0[pWjQއ-,7[ S"ׂ(cVh [0qkh7#<.n)Od)|OQj+gMA8 u[ٴFwA곷<Ϸ  a͐Ó1TD}"XԘ "wVcv"\Ʀ?\ ʙkWdOIG/<ӭijۂ ) N<1CА8L5fטb`)<ݪ`B h8A̵V.oo^|n"mvP *h\5cgٜ\/+jn~dP *e!A s^A~N7}M/o vʳ VO-{g7&iGP͂~!]]ALV*v)u^AxZHEP@xA~%#CAwْzT:hwvMLʦpwz!Iw8{ϰxt+/sqAj{HA2ոBU{gtmo/1szonP/Q֗dɽ7ZXؽuKl+Y$gW]+VcV9oLN&[i9PNשe`þٺi?U 8\'Ki o,6?aq#MEJ%~^S pomgd6m;bb0(01K?L fi@7*TaJ2LWjF"GV-H~ Y113ӈ YE1 MH"Xeլ/׋myj 0 %zWiUodNk2P+ڷS5NR!P%hg2=Ԩ8_Nˍ$ f>|i:^A'ڬQ@v6+uەjf;eE[Ѽ"TOGXMd))Te ȧsKh}NwBvBl5'@: پ0`:|*+fq(j:B=Itµ~ --e}0d#&5,Hʹ_sTP? :rm)emn 0ʣ7ZZ~.nu{/>%@pZMT5 Z ,NoHsHYKĜ:>ӹ@bO.̆^N;8rSsO-riuPwo4;)ƀJӤu){`V|kZE6;HYb7!}.Lm}h}%c#½(3ibܠ-Y5HOvp]( .U)][В+kFMݛcM>3?rGqƫ㽌V=0"5&ZPQCQd,7]v/6,nkzOYp>ɭi\#DHK£2)^rZh(fPSnҶʛVհnuO|hhᨶH0k!B'nKr0(?pE?q9b{Ͷ p[:JضGŚnHh <(!zG$ЂVd" OMGZF%16Zģںvi6)#`'B$7i_ӹyZOwUB ;Fi7M)w3a=/*& Q=E-la yr{e1HB"qpJ0O4nkyG"Y$n~dORxj5w\Vj2 ZH2d.7D4ȗc3Nϛ:~Nma=Ak(nKOv}p($`ad6M9KI [mTTqw20k_~ߋg%IDeӃ<Ԯ֟8I?G5YdU^x )M<( `trkPa5&~.UP}+ZkXt*©%+r\,sKcEMmА)o]ToVr5=Yf3f~Q,d;i/D(JVR8Q66j`)6?0 K'"1sw)nF̸%HerK^q ^;(+pV*C%D*߇P6#J14Hdicq lHc'/:W~#yK$Z\kL. S_EҪ(mJjRd1CWES<Ӷd>S.bl (4 |@ )Dz|84[:a[Ag#|[zgX<0+RQq`ҤfRV ETJN&O9bµAcX&-?U8A`|dQ8L>P!Mo=r9XNAy+[< Jk&F+m[La+LQVE,Fy2P摺ɽ1WՌP*K3@>!Is>/鄵vɗJYq0sh)6*[jmre1?DeGl c2td$ yʒǽօ!1Ɯs|"rͱZYbD $φg#9†ͭ1wD-ڝTm~ E֪B56Y*rK5[@)[jDߗ#?-\ 掂$pyt&FIԓ7(t }i,3b%*s*#sy@V$W%]>"7;=$^>~cpm޷6=mD* ^A7+BTH:^ٴRK 9>v5ĥQR,=%^ΑvcJOX2N#zj,8#mcԵ jظ{6>'gFU`6uo笑BP4B)`:7uw,h8^G'u A_y}_ wIjIf!"`-oYQ\UTۺ/[ߐ"ǧ%^QàY`ܩpbi4[!2_xAA|_qh0}0n3A?I MJhf;M8teFC:p8kmh:B (Y).ZdW S=1<)e/4ٓ8OB2-)ieKp(jv)`mQZK4 _ANs@hHF ƹ`o{A ,QW3l]>cY2k[J1u›rۏ6tv@ (%v)k +=Oh)lkgS94ウp2x;\,A4ld}!ceyaݤG\aݮH=m< M> ̚$yc lń̓F`#\yW.k1+wwԲP Uq8\5)0fk]zA!i' D I#rTN;I8yyqPPJEB4Wh?~cEmS?$?xR '* UZo2 R°&I#fCtFF! B֮9v#oB CR'!^"[UU g\UXx/VzG9v0DD2K5nTbMi ̢hBBw3sj#JO.=j<ڵAܼ.0IM X7 h& >IĒ3y^DU e〛⣇Y +j/.ᷝ)?U$/-vll.bLi_%vcY΢k;W;az1QS&O Cl`K)jyL>)E tGnX˳60pk9Kw&i.z>"f{{${AɌNO>B*tJ[mY}H *Go-]a?+xYAwFd>~t7ʕJR]Mn)!غcSOA{ o/E.#fu"8 zU_sh;!xà%NOd/yv?0ɟ?zGFZ ryz\=3b쌟+饃*#H)ND嵁f!AMiO*Z@ ( %tSo;Lj#BbwD@{cJsM夕Y\w|,-PI>KU+跠SڌT4Y|{B"Y}rJnբL2N>NY^I~E%Zpj#FUS&s1 >j\yHy8t6!jP[hSYQNJ6ЪKʃnnӨ"(I>IbWxDa.Ҷq㮟~";dWF %k^ Ԩ%nhURӰcO5v,y O_.\ݮCºb9bQoT-h˅9m4,i8eg<+$$jb(,6ne7T_~AL~2nj#T[5TE 8Eo̹,Xy䬦p퐤./:QKxà vLG@ S'l{1(m)wZRCe({ᢌYwlb Du]T U@~,H\՜4VQK#Hэo$,9Aߠ3jPr{5mkg Bxͻ~?"8/Qg((irq;JJKL.8xpO U\8Ճ6ݒtۨV%eTe~Rb[eQWE٪٘ ܣEBg1M\? +=(Oou7MQ45@ICSSɓ7lKF8ti(sD0[**6 !M$׉ C昭&ELħ܀2*ҹ8AL~m辘ߤoa >鏋|f?*Og 4>AnީU"k5#{l˞7亯>6#ىQ~F!0yiYQd"TU*'$L?OXZ'BNE6Z\N.Ёv,І8Dpc [qγ*g XA}ӠSPUt?(5]covMe3UM{Ԉ€D Dd T쵸OiYV2$~Θ !|tNs [6ΠpϥQ Zu;&Q~j!8)hJr <u*gkiЌcВN`7F&0o|/UaBjiO *<}^W9%d ]]4W<4'U=+2bwxoZpOIĚP{Λ撢$D HAX6Бlh"XI 0^w^> :wusWHaD Ěø1{h_!zXY(frݫ`$+ʸ co>y)þkfX FkJ;UX0 )J+M^ĸ v_ jtJfX%zM;Ƶ9KF>sV(V633݊sIzuw[2\>N8d2g|Vx5H`q7|=\zS!=UK l st}Ҿ'ųWbnWA"ĸA|Cz1|i3o8FSlRq鋐0A%UiiطLѴ?+r{\my :2-?2$8r ĶFBM]ݴM"$NyHr7YfZLvj ;Ra% W&xY/g|\:bߏb s/p;m]`j+_]@% }>~ >U~Fm}Nbq*Gw$4+/nJ6 OqEoԗIn{!_NKYsn lm {ޭÛ`DF5=uB7".r5M̳wڃ}gc%ia65a@seF$.8/"4X?GdW \RV#V>mkO[rn8VGIBm P+2Hp{D y{wO/%(ּ"e>@Nbz퉁+[G"kݰH^mƖUʍڇ<{=V֦f%1OAE9 ^6Tz.~-M] =rӐX$f"A0n~ZGUEވ`szzAwz]5.nO]R%u75G4sDG>gyؙo !U~2PAߐh&\p bhM#z&yF8$#( NW4o0lmRxH$ Rv*hNͪr1|nd֚<+W)DEqS7 SEHsaSvR|A~7-_c >lκ֣x1fyw[k z$ 3 XI^B $=4Lb6ᅛ$)ÔM;͡~ƣ[aJ?p(|4(G)Ib\f^/=Fp:pZY'F;98vbjSz*asnܳm9@Y&ϟҏ짒r+wH$ȏK !EMCf@M(n0ª[ Y ?~3Y8*z5otG{Jd$*N%גxY(75Of4q\m|H p3"®Ln,FMJ Q 9uC1٤Ԩ F LC:N mQy`YOɬS%>FZ8oǞW^uǐK>@ka(Q4јAWK4Ymw'9aH`9 A+N#wLᵇF_dVQt8(#ZkwP"zlA/ݞo[= Z֓X\UVA-?P?"-BR;l?b .g6wg-\ZUOc ~YP[n􉏜p*=&4?Q6ڭxZ/I5q5qmӕ!=ڸ#[da^ 򪄎b2rKP#RjuݥE=UO޺_?½f֪_͒r>x%'#˃Z  EOejDVώx{O,%ԯ{/ uV5yY}iF36=ۑ-[}\~MƂlI-?]e&4wRohQ+^4QQv0BDM# Ӱ\?0 DObq5K$Wx O56ٽ'S |T9{*WbbXKh)Oߏ\ԏHElk']hoLBMp)k-"tX5+ށGz<5B{(4* CM2k 1F͇k璫1xu8 ]]tP-fmUo=To#G9 q ZpJvj:>Aa!4 67engP؎Ј.8DX`F`A/; T !ƩU-`F>'][:8weH<_5bX `mIDЀ`0vByMc-t\0?JR.Q|nO^$tW'fZ*EAw|ו8֕W !7 %D%a9˜>F61@Z0t%VNYGY 8_Wy+3;2S=s fEO S~6 2g$Hɹ&fEf!?ĪՊ% C!o3OM,I.-|n󠾲оmm1eD<^R9CHG(u1o`0EU䧭^Sϑ]l'Hnb>jg,~Z"g_ˠyi߫N5IPԥ棯]3C'mFÛj꒾ypEy t:_S_+ƃƣ2{ o1:sqI4ՀҁҖ'd , ?k[^Qѳ '2I=I'L$l?I÷$ 6+_GJ|İf6­u3:2I?ίhò?ITu8}<y8OFt1it\JUQ }zx|ώx,V;Xa*` C`0IQ/._eFOpN7$}#hQIyT$m{;4R|Y$&蔸W&?4t_EL4 v<H#+ t:^qOGւJ[MVIMl 3?j\ztbĤn5i.>-jprcIsuǓ$sUqR^u0bk \>#K&Aj,='b7M̋]ăp!`nHe-o1!,6! ^]16c*g)2=t$5R$̥'#`ؖ% G5g.сrԻs$&Bŀ9ߔݡ4PZ?Jn4| P%}Vʭ;W+BiP9E3kR>Sժ*PFuLfunO*\H}Ŷ]WKfUXЇB9KMA,p{WH5>< 3 Vl 5P2iuwEk{ e!CCVFxrJ| \s{Eg@M.%̌.Y·4a7~jfB.Rv2sEWo¤ﺢ #qaL+V'~B !3rYHؽmZ~-7vNLsܔМhp `{|.t~[c4œ'mR_m\gS퇪&' ༜~Fyۏ.ӵxB/\wv$5bOr,/'[aK>L.뙸wLH#q8p?3i5n@iC{J߻8| rR G^T3]8{*/^@})-@lt\ +|X<"P[+>ղ pZHM>tDԁf[NIj} CsH7 bm%etph>TTG0ORr+[TL1`НǀC'L[ 85JCiqs^Ѻ`;d.h[ōrȼH8iU1j OcA?u2 >e0K&uϏ"v+粯4.o)hȴ ljPO엄 w-m6ur`/̬Dft$܍%VU!xY(e !5͒Y`~- b.pF)V/l#H$FLfNz1sDn>#+l%"V\=hIU^y! Ti}DnpϚƶxoF 04t0CbJY}ݖ. Ef`KH+S+% e]>I̚k SjA$517>?]лCjʃ{N7`z5`O"ΰy-;Wa*Qf4NN/5r&5Ǧʥ59fhGlc(?D o{uJJSb`8 FGjOsuʳ5U7;INH+dc| {Px# tZbZ3s>m*~'$6?Frzh2cI-HA}t Y}5Oz< YAj%hVYe{L[qw1"S4/&c!n~edẕq8: *Iݘc|h>k<6h-SMD*h/m{"谐6Gn :w9Gt\fR<lZF6UKCaL;ܶ: 6Ɋ}3/d7Id 91uD-uti&0A cνآw@y槝4\jnK[DS|햘NV<:]*R%}UЬ1c0Luoyz;4Cc݀+.T4f yR@y,NX`o_7D C5 1qcY~z᥆\yEQf?#X8crei Zl%]D*$"` \jpNeSR'pq ~G 3p0}'JP3 "͖ղ/N,W`Q6ʰt]}{|ʢa:`p_ ULjt/!{}joH屮[|Œ44Ę&ZkCĆnk&U[@_W-'hW~ddcQ-,z<|vVݢC}-WnIzS(<3.VztvNQrG\ \3<zAoi졳,C^˟>U\<;; *O!l'f6.6$=xFQ}c{>Kq.#2vm{'}i`XSe[3u.{W:ذreΒYY@@Δq'ǐR4x2r<`*m*_dD!z]  u<#B*&Żu=Fyɿ~(}?L¨>R@B= [pB-2(J\2қCw^Tn8S%*A ER0T4cUiX{l4h ԸϝƮ(1T$E)>oI.0܈f3^\R`pga@|m26 B^-٘-CtA"! >̖ Thu+LY)Jh5ݎ2!-+‰;-H l'D,xbOöb=Ov`}y^#֓& x-.?/0"K8uF)+WG@{gf/HWZZ~b- Җq^6hM)XJgH;\vE=fMإk%rUAɄ&sl/tJz/p +;'dLunfHdKnS ATVD @OuLW?!HCG Mv~ϯ k5FDM7#2 ]:+0jVXfF Znr#zJJ-`F{RZ1n yӂdP@F7@xVVT""pB8qk-b鷴O񦀘qFxqqUօj4]jEYB[2 WW kzFK5\xvy.AZzl4NL׉D*# ̽a%yēg_[KbIl8l IQ\0!= ̞|}̲1CYX r*^7tyShPF\48JZ*WG0IqRܦvdGH g (Rs~jr%C?~|ַhA%Ahd ڏ#hs{=AL'∘gz A<5)Z1gYTxe-44GO' ^Ĺ|OlBYLGc.>nTWx컾,8{)+VPN5_OevJ{wH J]KwDU\@8..p`nsK˲D>0$Fu qw4\?TW?@ܶ9>-Gop[7xQ䝆M896QtO?N!oxxj)v0zN7%>`0"O3{4˨}Ab 8%ZùQ6 -bzt<kO:Gp/ß??i)ǯYV૤=so}Өܡ5*[ \Ndj~R,iգt_Y%尳 E˻C<. 9OCh~?{|כ?4ʊV'}ᣌ dD(gMguQ3h,TҚt[3{GfGZ/CNLPe1oM oKMX&w|:#v;;,3^]D@W\u?/k QM6br .swLIE(`o \.ԖLr% MmCvlIru𓀆'Q<"~'2J傲DMͷޚ RΝ_y8us'T>ɶ.]wvr/#щY뺤2>֢l @t.d:LSIqÑhif+[aΈUB$+b@X1 Jd3!c*pm"G̓0bsk J21{5]"5cۖϙq\Uvi̾? Цl3N3@Ԓr" Ln@ xٹUj^Q}M׉Q{O}φM;#3+D(kI%*>1鞉*cArْ(DV6Ec6#QUriV׍ݝˆRe&gZ8a;5?a 3|Kҫɾ,Kl?͆p%P͇ ׷ɫ&V4]8BH9= >?`eWEsd-N! q+,;7ȅ|eG>p7uDfފ2鸭 ɛ`NKa~m_nj5Pnv>.$G҇.Pk'I_ Y]>Qg9tϘ i2ҟzåBL?Y5sI>.ws6K {#TpQD_p "xcgp*NӮ;*{,dJ˼2R/E;Tf9346k覟h[#L{@ ux cD^ϓ٢IkF*io%r5*jiX vLV1L'`O,{1}!CRYPQ-pIr#N͙[4[6ۘJWNjiYm7w^;~ckj,jKM&.%&5?e Uj$w(O{֑݊@x9ِw3U=Eޥ@?[YQNYQT+AFd0#e&} DSY(&d,6RD- WC>q$WSBĎ݁w$4FD0TN XʁQdG ߑ҈/Ϩ (yMChyk'}cP[A L4;:&5\3e4:PKm:0rY<;J(="uChUIV[R.߷ȗv}`ŝ@wcy!DҸ\b]%(;"ԭ+Ki!c"ڻEVE])YQg#(ldž˥n.4/p^F'o>.O]Z6 1j7u-+s 1 $#|NΚ`K"Dv,F}k&r&uMrgTh+3I]^J ;i G€ZHXI{&RJߩ^b#3lhśBC $>X|BqD5֋^w|s #Jlɋq~:x?Ȼ|&ڨՒRF]ZF.(S^teòE HKzPtO~ Jkot7ѐ_n#;=(cA 1M=Qs1'}'0)]Rtܛ֢?麼PC?YHPȒaQBۨ~o0ϣ/ d@ P;TSHem,yrb94 ^hۛqMI&9cIqp7> ƫ@)i!KɴXKHx-4)*xI-.hjPh´b]dM:ɭ>0鲲BoTÉvY4:H#4ѱQ:]FQ8gmcksdJ`Jq7!,b:\/sxepecR\z@ϼi0{qK 1pbm>~x0J-~,BF=eyDk\ggck# WɕJƜ`u6uL>(rI5 rl]D`zd-ҟ~GD\t->ƤV8ybĭ4 pޥ좝BO"]p,ט7mCuxn o|5 p˻mWƢԨBRf6qR@u+ڻ7#0\,0Z'-`׃l[(d[} Ʒ6~^!T7%z[>S_ m|'9+Tk=7]٧pTuF+.pu%ݡ+)Zy,1DNe7V['[ߞGKy94;)}W48eGJ\ Mטls k@,ƴTIΎa|_/w j oT1 03 l!5ha,1}xZd^]zys${PiHDw'r禞zh4ne~7clt6ąet܎ } `>roԷ$P=wz5HE-<߼=@t6,գT)b{Hz8шWj4 B FTsBYF52ey6Ծ'wW>²3 $sAu0=6\p.5œkmsRuyvqJ8GG!4%д6av+ I\^k5rL)K|s,fB1a/B[YsL}t ;Ib&ߕk!hlșBT]qr-Bʇ@i W²plN{Er\z;n+†N1cTޛ̒]Tcu_H׶6͂cNOhx`}Ĺ?Vh,&_ ;˫me߿@;ir$\=!:= 0T97;/ˍd3Sy@H9ԋjv- yץB)Yꊕ4;JZG^ן#Lpji LQeiM6cH7kϳY᧓qb+H~f﷬ZxJ;E r W Z?Y{MNǽ$5B+Ǥ+;AI@M.Yomm@|ORdrapxb_)}]7 U%j|JbAi]J0;6R|uJ]38-FeZRR$Q`qNUb2;cBMn Iu\Z4 _:n ?eJS`>ݓ&&ꦍ됌Zġ$)gx>˅ͳ[E`K~Bw`gAw$Ժڣ7r/3y7 a5c*T㜁XC+W1h=fAKPJJIh#$S&6U2^sAWѩKRy_A=iY ӎݷWt 8·ҧqlW~@[SEݿR'_YUNaLKW"KHϴ-4M $H>T, 郎 I4co`7+٬m'4}ؒ \xI^Hu0W C#QPx Nj:?BAc.?†dI0ΐ{ComWwE/mQ/{d)o4/ ([ QՉww;]tZ'KnF;[]VIݙFP~U#tαsy6<у!dX=KMEk+}^0`W'*yShɆzB+[g9}TZ:%.TRѓz[eg;fJ%CD5&Y%~#U민괗]P/06lXqmu4NL 9X hE.:7XpQ:1Wzl{wy\dtLq1SUY6|o\u5}R=^YڼtRa*Fw!55xXh/w=T(*AŭiVݰ۶w\bb,JشW*%c^I }%tըQ*q!~`x>Ƹ'NTko4-wG-.ߊݔê$Tx,DGf@Knfh┱SNش#86 2zrK.QD@7_Z1l] $۳C8ǯ:FfdZ 6UHjyka$jm<.+& .͎%֘ 1yr@-^*h"n?f M7-Tɬ+TZkeW]CtCߨXK7֭"wT?ʱ:XՆ Ȕ8ˀV=uB{>.Y k,t)ou~x ߃i/BQէG˾3Q1BM\11RU\HwĿ8!*+S.x5)9x?]P)/^X+l[G"EVIymf7۝xg?mVыN5_|GIQ:mޠ{=:xO1Qٰ+w#21$R Tf6\gYd֮FlXLڡMqAjQ':g^dn(Œj|U4| k XF" ȘHBǕ,ec%o]Dݚce)90u z NNZ%M`6B+p<{>}|ř-sNH(Hsf MsM>!f|z15P?σڝSWf8E !6ݹVY QT:1A9-" #j.kVowGaӳfǤ+ƢA!حA|$ev\6!{Ja$w]H+UUh0>/?-Ŗ3NcaS=e=xP]Ǫb}zmuUX3L9 eق@B@ ♮ؒ!?k )C%}f5^VOGCTE K(^fH&wtڊ'G{P⯣C\kdq!ʝIcE]'6lW.r7^ X+k˹(zL}ՋB.%6l:Ԋw>GLCfiR7pR**8^x`Y'r/sJ[ɓѳ_~x@u3X"Q~{d2wSaI۔p0U?b ?K[ZgA/^TP)ahVZ΄ۦjNr:H @ipMs,flUN㪢Kgu={"ޭliiޥt1\h-D"gۘj e>Ŷz_[ ;N}lu*pJά'di])C2 R׶Eă>cȟՆS2ǕANCLm|yЛ'oUg#j~3Z+ Wb{KwK#1 ),8ѣd==f$6 W@ex,lo3YK[T6B%O. ^B`g.βEuT,Оkuܣ}v&CTH%B[}gLiͯ;oPO^S#9BF[*px%))CFˊ', >IXN(B6*N$Q"xzB9Vz,@c>չC$Fz@ >TS*pBq =2)t=Z c487Sf!0 Nf%p2lܻPQ^&u;c*gjS8Qc%= YA'ն/Xzzov uԑ7.p[0'jN5%uU%?/ ?d ˎ4NFx f#Fg;՗T<`Bw(}_9}RɅ]Cv`P&OJk y{R s؄bӭVe\v.BnNr܌{3n $}q ƐOTS^(lPQ <q{wRb%a0Z/kڔ4ӕ63&_rc '} ~y!miOC;Zc>-].\exQ?Zkp g*\DDPHN!D'q$SQ66ʸt151B$* /5tZ=!=3u lf8M,Z/F @H~5-}'ݹ0ק>JȯN 1 ߫ϻosZ|gCX`lօ4P7LREMRZPrtq>N*GƼTgkQ2.ޖʿ~kM.8W& 5GZsՁWR~ۢIOGxQ\&'?#kΰx!:cqe{hQi|8Y֖&Zxm8eJD@93\mC_:9" ո<,`"BEJn?BZI~C]r"EX~rİWfu&n^4{7y˜XƳ_OW߲;ԒCmUMzMED/Q91<2TElHS6y}tFJT '-.$ʅܤF@_첳 ?N)Aܼ@4[ Ff#y ?g[g`?q>ͺ)&ċhpgΏMp}[hrcCR qRD:4.<K$\= Lf'NT}8$ rf ;J֖vꜢҺM&6`@&\]V᱔( wo%U:;/nLK'9q?=}1xO b.0[Yn)'J9NV2Iiy9_mv򍮌]If2on[a?z~A|!hx%F*%}|J~_< ~ojK3Hf ]&뛎nEdIf米!ԫ_E1C^H40vk2kY0~_@2BC ek ,t:PzÎ8F bt2r*XCp{ (,3O4#;Ƒ<1#@orà%H.k%<4.)EѽC݈nֵl]B: lz&7D)~\J`Nr 1+eآyV% 5*i:|zObeHIcҡc0ԃ՛;[r"o5 ˓]_,>onvG$"-nq0[Hb׬im?vn%߿>"zx |(7|DWo|})"6QYGe5Aj s=ȸ]qNjx I"譩z:cZWF4J=&, ު;rsOI-<.אusۮf̖#mxN̂ڃ&`*"ɍ|aե.#5̭#jƔ UG<=?(I\ u%%KQBR13V6F K-VKsp-'eh睧:rJݼVX6 >Zˣ7dMgUڜIED/ASSwLٺhDBIy:$rsnMC@w5RsH#>yiL5tfL2}^F KKl&qob+#<2< AXh-Q])B:P7$.vZkK&2gN7d*i OULa71u@w^_~ĺЧ 2rGfφ`6l雄N0#-sL81pLjᖙP`Iq-EK?5 ,]qvL#Ǟ0Bk6=K?Gcg;@9<%Vڱ%AEh*zV wr).\.c~1/L=;*D ) 21\C[ީfDQ|Y=+VT)]%n:BƖbap:H咖w?f=D!uN\:kxzlHbvgڐ̝VF7f`5\=6wBQxC)ֺN`}RI"?ԁA~ѩCd{_ J=<7?MCv@\pO/*]t.1HB6xi֢*+t7:n͗t^j|"jdͰ BgŦ7f{( SC/='¦&i/{cHy(FVfRVVp{4 +9bnS:2 q/(8zmRu dlϧӠNnH,QN\_[E)`^sV}%1o@LĖRďǼZpFӸʲV0&dHw:Ʃ%}YauqjȕpgZ؄sO^=[d>/_ #'`V$OBn^2BV&V[fRgvBZ@*&B9QPǼ(#g%\ V4(h=;(0nlr3טXLh69iFIXԾߝ{%DmS7J$ׇw7?ZAXp 1wV7u9#]W;$`CC8`s^Н" vo[` 4ѳsvʱ8iJ}vX݈Zj dc*)ekȇ Wl7اG"mj0h).\"45^8q0:?JP˨ckͷsRCfXLRPq*`C olvrmD0^V Qʋ6"õBAO65)O}[]ݮ?hew׼* E4&duT6GFoqB|9 $m yZQ^cy 0Ǯ\m~.]CZ0 S\)ʭ7L̗I|Ɇ$0.–}mtBPwTT8M,Pqs'.W Ђԩ5s9@+:`ŢĥMS!Jfgd&'O`Ӆ.@j_( fz-7A{]p28ÚX&6,wTCzy0xNOkkf- #?T}Hrߨ/c"?m^Ÿ6_F՞R|x)ڷys80Q2ԑ)GPDZn 泽lN"ʼ,ĹBʐk{e!eTB) &ֱZn>V#Qف_A5R+7i C= dlMގGKТ÷!=Wm[){,"0I QnҚbQsx),Һ*ZP]UPfZ _7/.3"Iå//AQEccEgx180ybٱ->1N͖؁U2İU='$9A.Gй*f\'W^ فf\GןCʪ1=vU Rs ra!ӽ+rer`=@cqCi*1ȒDX6bzN^dϓɮKl['S˗pCI?x_MDư7N$~. [X0c+{3}w_5͸;{W;K1re`H*dr&J4B_J򕙄+cua R/װ1R6Uw8h=Z%¤ɤf,Nx 7ֶ~~ۗX_ɹɕ:f2+۵3Ρ 0yф7iҠLj ߱ɹH#UN}†+vN\x\j86 [`(䷵D5G42ct2'y</YJhzc\bo]Һ[~sxD;CۢP40$Y|=+x܇nr$0bB+P) Q ϙBAsuV[ >O$ʩ; .KmWecI>DNEbm$V`[T|sCy?9/$ RmNfӰmH7Irr&R^rO ϑ~0 TPxb+vm6fXk&:=e;+\VD*SD_솵;>g)PFlI{ o*o$wU0( Ji% BtL-܃%f:Zhvg,xlENV*GX>x\[_OwAl) FU$栄sp ̙~ܑXa?T',YYAqdJUdpV-κ`tQ\slO%E/M;^`-NUʒQ&񦖢cJ8ԭ`*'Ÿ33&9u}2c軴}va љ = mRK3.Gs@9J2dr3U[Ɣ骊{jLV1:ā3fB{- MHvNha{ѧUFo$ϵFZÍP1禮ee P.t}4gkKIL܃C$fxbVdLuEʮe ond.猕F?Go Xoȡ |Tn a E`rpj궋Զ\ڬGRh ǒ}Hoc&ǜ$69J)Ԋ7; TԄ?`(mKo@@Y,Ra)P~XKtsڝ!n)^ Kg€?^ -`ItgĠu]l=t#g\u o|yتd: &z/rc(F_CѪY+.f! q x^]ՕM-@.b׳Vj2Z{FƀPZ0ʻxRl$p0 _3ॄܱ 6Y;&6ŝԨ$LmG.ɲɳU4Mn\O^ťZbhOEU2Sjň37]neIX/˼,pgD;ѹMLx٪0gJ?7ʕOsx_^VK%$=ht35^}] MJD܁@v [#rCk o4u)M`z|{'bJwM,f6؊>xUFFln$Oh ~O\}VK@.j efPcDz:-`FF?9{!Dpw0}\[9H#~xy 9kdwR;{\Hl^iT eyԋYfZdRnr\/n L7pJpЛW1`~I-%?&/:2\U?>+"l)%/5xIE,gγᱛN7k H"[U+:yCCVkTp5wx͛dO&"~H vIwW=(,#[–ԑz#z>RG&%EY4R8-ǔ&h\IQ 8VOFTA*:ឬbIR%}!5U5bUEXf'Z~,Atf~nCb7D.]HP? RT" j 3,rplrvO;s]8B)07B bWa @*G6Wv$fʽ5H4z@ 5dJ\9xEc_n/N\'^dgxRާeNXk)mv9ϜӼUx)j.?J,һxU:XR߀g>5G舅fMjGX:4R1L{*NŴpጶ*tL-oݤ?&j;'C[c#$PS;>rWrT'OJGm*Tډ}y*;HVC2$L"UlU~_+@X|%=ntwE\JHԔ cf.ȯ5тJsai()gc_ǤZTI4!H9&e 0u3ŸfHEj!F oNM\:38]U/N~'xmY-h.\0.Wi@$,H'R'/9}sDbuaP$f]zuOSQUڸtt(v8J)Tgd! x+=HE>7R  ~y߰%hTJxyqTFzLKq>@phm3UCe+!N\rh`tdSz#+ګ:<"HʫH~#$3 4-I=>L)aZ˔;o 7bLP$u@7$$ݓ]*m8=H%]p(!8-WUد]G ň2Wg%^8gFKaO[l @8œ8Ґ#5vIH6̦,]zbEQ#ȿl0Upk*7GA$wU6;~~ 5~V%؝~ \(|Se.۠PHfL , oQiooW|y'g(?ovE0 W{8[R+-W2a k"(2/ޙ^6(.$yCfoU2o$oajt<_F4KA\E~P}T7ªB:p9վU3xGlAfH@j] cn(-ZVd9 ""hSX>U"j>pmtC>\*A[DPFYG8BU4Y,_"ڔkO~Ir1M`Hz?eos*i;VX䶬x[ybɧj,o5_t?BUM49ٿ@Ӏ+_$_B?6ST% M q&l;NYZ F|< /d8`og[1W:F7&PyJMLEyaFwL1اqo=%o[?*d+bJvBZ&PjEH~u +S>J \uM/@Ź[5`3sٻ:MGgbf鱃]ޘ]s֌jY43[dݩ{-j?/";AUL(&%Vd'ʉ|?/"?rL!Y>i1%R tgHa}ۭKpԩmmp6;p_+^!&|D0TCahQ@țbE9Qb4Z sUά~sߩr}2ʒM_D.8spM1^KZ<>(^%*ldn$(=[Ob.ؚw@XOO֊4\ҷ?ܫo:W>*偼]OΝH4pVK>s(-3 QS'ZbQ;QE tum{"7o#ܼpf|O/4{QOX>"Q y3=BQ0/7 [q5嚅oى*ݰ= #V P;tpIxMfF~ږB:7,"ZF)J©6  Ī"=gg F:>ދpT'ބ'`%CiRԵg.ש%p dA6EѯJn-0MhWu%1hw!=.iŽ܁‡5h:nN K6WCO+0 619W]*E'az+.|mp# N3r٘b.CO9eATDAJ!1ۮWl1aM';*<:N516(!ݮ=|/*>4꼹G, "Rc+H;'S|7 tp~OΩ+)^L>UPu=M]JAR)e x`+9"!NrGXQ|(ӲV&댮H6[B1'+08:d_^:y_l [\[!tƎONHp_ yn<uem"t8ZW9MMMwt@v(1Rx E BPI%r PӦD0-Fs Z5>PjdeROe}Z'.5cL\|>q}3I>3-ŀCi}IHy..az-2?ܺjY#Vr-̠")yq1 -d^ M@7e3s[wlӉwnKь#vCE[Oj%} SAY̆>0mn;} MEEkCU251ǂH6'Xd皾tSD2 +\hSlSt84V<'G)uSCd7#<˿VIXeI&V㈉0׀MN7Dm\- 7gQ6n|D\ţc7.;RP- j!wDĎr܁8ؘ׏X> [xB C";70(DqG*Ql_is 5o* vߛ8A]35Bπ1=9}++ȱDp>ƎK ؝*;`pE^he BmX XGZs$jA3]7oy13H*9MZ 3GerI[}c,7U{1T-o}IhXI%~Lxwʒ=9xE?CD㇁6u~"թ*2K iKuhx {yڈe^;cdB;Y&=2AAA/6-7.[̥;_UF;svk:0* *@~KT736*Q\_z!ĴnTzIqwh".oq)㷴F+>VD b܃ NԴ,XlU~勺EUǼ >t-ݬw y4̪,jg%,8bcm)A+;<?NE cC 5n$ڬ 4T+&R3o{QUM$|5tD;JTήɡd̊s)G5բLȭ%ar`޴BbNz*#~ m x(X +k1,lآہ7!7vu?rdRk 1kD#`.DaEx ůs2&lpkBw# _m-bF.93~ZsAHǥ_Z/GOhYSO+KӂM(dwM#fs?vs9*G ]HAy ) 1@rZjFDsi^ru0'd+u銖R xI-՛8}f/~ϟO*i-NШЉy}j*2e/ٰp-*Փ^ި ؃gOPwd T`zOH(pQKFx)eJ6m503Ln$ ?Ug,3`Ě݉-74RZMa(0S]'S)lykmp.LWDN֬b;gSm猃ǐyx ۞H۾zt=/}|AFNV}~њrU_gPm)N6ė"5"%ʘ& :q#E՞`01|RLM[Lj[ i (kO(R֑I~5Z RlI˴NpÙJq>ytm,JjMb;l9iѴn_OAÎؐ,v(NJv?qSnz~'{<1͗d6n_z#Jf7}ۍ4oXO̷GSP~ĞD T(Ip;=ΓdD]J}l7@SL#b #zyՑc,z>b7v&#ܖ<{("iJܧ`az:ULP6o=ܤҁNm_ DA|@-fh~ ,9p"XR (Ft6vz2Ţem$999C[\IdIұ*\ u;hn<缸wщ%*nm=FοK1'③{ܷ&md֫[;c& OfCp*gEz+KÑh9 ZT^܏ mس\kN8OsQn?f΃h}Fi{^#vS*Y"}k0䬿h<8\ŚGYy3OԪ8Fz^446ngi']5 uL~*]1ۙ\9pJf,.g}rGN# ܺ.[ܭRNM8IX,{;J1%W}/K /!U?ѓ:Pp d 6iykN(ZCJ6WlH/m &le,l'xEJҢ|Im@g.X Jddhvԁ}?ER٢xY3l-s?: f9@忍e"G.Kt;JpAJZvxG84=*Ym% Pt/D oqC=[(p < 2T ̕.Wqd 4;H8xuh-<2= K 11 S8d1;q0ϑ }?Q-/)"d3ReXEnfP\=V[O~gxxqgHcɠydm/HDȸHKM@E*nct,9"yWYduHzx#e|s|4k436w!' a"I<*>cKGT&o2q1%?TU<i.4!ʉR(fmR7{u߂nXA.dmԵn 82='yk@cӛTZ7D. 460n" g>fD&vAY+)@+7 &FZ fJ?Lj[ LmUoW,PK'Ѧ)ӭwQL°ݘRn6ddB`9o ?k{HX.ޘlBHϼzWNnGW_"bkUjYkP8?bwyxd0΂ƲnkC}}bTOH2$~y;p4>z-;8{Z^FtW]({3'VB%5ȈOd\ɥ_rA^ן,MOrҗ\UtC?(9kTnƭ7"ͼE9{{.Fw  ĂbjjݑP*gGcu7B2t8 kFSRÉѧ)E

|g F9i'~dmׅi,oZDʹ)*n_fڥ2T|:鄻Ҹ̠fXZE'5ҵ$e<5aqW08>kAӮ %&MXut&mRDjOݫܐeYJ0ݻ="CCRh{(/ؓ՝~`GHǸu'꾴dF'32לâ@'!vNKe>,q b}517bd+≮SK/Z2NkTz~F?νʤ=# qy`崙Se:% {U4㸯 9PVd%uEX8x|,+ȁE z~ܤzzBxhU|)MP>O6ۀ$|}DMjo"Wjar3o Qm,`xPo3xq3xW\>M/JV&hճ_Lr5A8Von.~}/C81yW.H6͕Eˣ;j e<5PF),FmfL` RU.upJlM #pQTP_ d|K Xlơ^=qXo4л aY|)(zK_N99|7#-0-dM`s.wAX/b;g]1La-hyPm5!]^]>~N<QL!bql<zQy߫W6Qgp7iCȒ4 g)˜Cb<0 ԉ7 oZ=kթ()h/ujgN>.):;;vZტ>cñWZǗK̒rX!T>X`Q {'T=눂jn&Fe59}f$ K^q Ļ>7Ƅm\@h"@WRdY*L‹kTBoQbbY +J@!4jvʡA쾰 Eh 7U <FSmV;gW,xU"̍!0wohUd2@Hff ͬ -1 ^2曐 ^QzZRe|VKyeQ4/0ׁ!ufD9a4(1&=~[i.č-Q{o 7XQod;f/8:{m^#37 j7 E;j14]U@w>F'WEbr|.ִnn'|I`}EpILwo"qn߆T ]ў G^1wI*; 2n ]uɚ|`;kNTqo8E6h =hfuS*o(!6`B.`udVy^g7M$'MiR縧).?RC e{MūV >lEp-Y!o 6Y9XX.6m9mP(+yN6ҙ˛Y'n)z~;1]\:|~Q"ؚR_eOcWq2KM2yޡOx-qw K$l̝ Ϝjix7En[tn.f,b(26c\$.22Q^2!Sginr1f{^#cޏЊ$&{̌@/fqhMs6q~]44tC|j샑"!iu}-8 ME5EĢ C\'k.{z jQ/A9C, RPpo- 3`]eTo_Je!pHO4ljZi;}d 6-.C >mPm gɹTɒψŮ=6lZ"ډ,pTMb k7Hyszl0C#LwX8mt31|@:&b3*\ o*ۓ%^ꚥ:~6êOfA 3pm־la@r~}x. $!XPG խVJgD_ȰO?P.U^hDnNq! j9=hB=4>@-/Yg%ԏaƍko0ij\$7,Bd-vDOcĎ@sŧxh~Jo`ͪ zzS /Y#L;+V夓z^lzML ,1dFHf`E(} jh/L͈Á:|ҹ>'$$żw`vB$ UwK (/(4u*NWGTQ75{4'Pu*x4]L}nZ%' { jgֲ&_ǡ n R_b"k*jqw""+*WvЛp{+o%"1eœ>/!ᗠRxŎvG`KhSn!s|N@A4mp$;auFڵ-d`OJb@6~Kn{xd{j4&*Zx(|Sb)| `"@8# -[ w֠<˴Mm N%5`alp7ڊ|}(F?}dZaa3e@gb103zmݶ" lf gzNY'x7A!L:uQFSea,{tj{\4!Wv-Kh7CZB$ .|\KyZq;#2NL| NyqNEnzFiќONRRCi}^?iΉX# UQrD@@1]H@|q@풴2TՆ_|V#~:QJݎj/E1Li+ᯤUV bs&_eɝͱՋ!IA8GSeVbutQ;-;8r t`vX?4dnuNHO7[A2o!?1zz+# ɺ J7uJ,E;+ \U(hܨ/T'3H@6x(] ib>F=HTpvnq}4`esp!Ur6@WkEy]K}BLT翚{b)Da"&4Jgzx:Zh]AjylXǛֶ4rl8⸜nU Rbۊ#Bjz9MĞ~m h\ gɔQW\."b%ܥ]@[݂Xf0j6? k=q_nTE6|J|RXsPAdg"OpGl;?[okk!䊬Yfya)CGevsVens-oɋwDx`6 Y88sStƑnSktpeV~ ׵mUԽc1lX]bOfǻnyC{4?^ɫ_D8՝șwn(~ 5 -XzvH@؛’AvK\]6)λR%joVJEO8,# v7J+gyZޢ0jdctXZgIRw}HY/#>'3}odSm3&f 7qqBJ}u͆``P-(+ B~K\aN0P;!tNCF8Y h#.1`LV}JP\"FJ;B 2L.:-?Dެ$S%kp` 8ZVNA+DYM5XuW*DC5-cT7vS-xblUa"yuRD7qeF/Q71FxEJ87qr z,:NNO]1tlLN_TSA?6ֈ?tfqR%ߑGʳ\z K*k01N}r:7̻-nc dzy4>$R#}w~_f/v΁pQ ȖLmDJPO2zՠ/Ѷ;WzBtfHЅ9ɉ 4.52l ).Wk"E4p^'0/R 1:-¼#[_L7"S6~o+j%"Ձ:ՁJ("1+]:_nPPMlGKb_k¸E6)b;l42gofA2J/m+t2JxjH}@q=73W~ ͊r ^ C iO\E"@RQK r;}n0FiKE*-5 N֑ ! `mˉFT.;jdقGJem EX}>9?6߼2wOk4E6@^&mJ>š>_>ӞV1Y9k&?ZdfőcG.|*mGt\C?^$ nmV$jh VYs)|-Q٦Z~X\pu$1'Wq̳=KB">:_JÄRҙp}Q#~"lAYNb =JH\:,ĐX4-,/y-ȇ.#g!N.sz,qiͰ𢺟?}HvPk[Qd[\((kj_D-us Io2eRH50樽jSf&rGzĶD`kʏ [sv[ >b›H}M֬q0rt<^BLPI'K}ϓR:(dMP\o}Je(<@#(.A!Op w[6Y 7}=XRHj=[GAZ?G(Pl"^ Ĩ]ih'V#\m(uiȾ*atg?105Xr9Ho{ 5,~U^h(2 9@hP婱H Q&%";[ oA vφ`sZ.8`0L'qM֒~)&jLg,^}IB&{,@*ȇK!&ipgX(43F␋']E!Hl3oz  7PBMEF>]EbUl7,=M<@КX-2MIF_5Qʛ쟂mc{:eIl:Td+M =w,.¢<):!'1dqdį0'  cS.KU!(M's(U܋d7KDT[ :x}}FG-y c(|r_QT' y:%$W-@BdSmGtv"-go-tMjȶOz/܃ߟb{;2q"1Ia*T(Fm1^_KٱSLNa̼fQZ}9+8w^#L0 evG_/)4)fZL/L;TDrFڊgخsd[HnbHE{mF #IKu& _5l-F* EYzd4X#Kַ&% 5D16TV1$YU-h\+pr kтQI%Tѓ Tb}5~`m$ƀ>DZ[Vq&㸌"_,de3n %ۤI` s,ȭY]7YfJ)fv>%X ъ-_ ,uYDXv wdZjSǤv"}#QdLY%ㄫ+WDThMxf/D*IGAcΨ|p,hy8v;8mn\l{#G[K<V̫6jOCO/VmĕU"u(*<$tRүKx,66)0GH|ϗ4w*biڷYEo3^U . {L3L-xHl}-sz@QV>aުv@r(p&VGNh'aUnx~%["(g3}E'Y]U!,cbS>6.u9}zD9fVE;s!(ZskDg*AI1O(b"#_1ɞƷrA]=qgTS|8gSs{ݿi@`O5\O g ~ ϒueWxgd,tE歋}@JbK׌b̒Z.Ҽ y 9*>zJm; r~lծYsHyDh/k)ym+5jޚ]^GƱYݢyZp[wk +i6;ăiY*lnnAg ث}屖I8\x. <fFPI촃+8Yx#m Z~_9 1GA P;\q{H6ٓmI[3[뀫(m L-L>m/wIIٟۘ rv‡ "sPxJ>e纭1@~ KjiyfY* k-r=&.,h'CD]W,aeZZ|'bi9$zO눏͔YcV$[$a+4W6:xP;}$*b*D*< `D !ʾ` 5`CTҎK>&};eǬV5>,ZZ2dǭ;:>2x5-PB)쏴1sRΜlʑƜg@.a!8E؏ڵ}ܰыq"Dwq xÓ,?,9˷>̢'TrpkaeblQ/*,T(~@ \ ڸwv.-$hP %oJ~Ũ\} 5^, yu0F{bتIZi1(yU;@2rPQ/TG<1' A7ݛ^5t?Z@tI+ ɫ}MEc!ޔI:;~N-x}7uSmzf!OఁXUR{EV4'mҀ _paKc*JA'&rlh9"Wl[PPdZO4 2*de" PFMpMQcqIy&4dԨfqcHsW4>IDu4_\*H⯿6?Ho&ZnBpuԮ\wIYUw vPrhI%^V@ƞʒ &(LWvD[](pПegKONfLw myyyq͐lf%Bڎ%^8+ZA@d)֗8EȸLGA^rxPEDknZ֐~2U2#J!,_MLNkZ)ߗMB17"*)_SXa ¨Po2e.As`k~Mc,ܚ-{' [D 0& R6%4KF#Y|'p]][Vgޔ Y0wl ~dRUϑ9%88y>.wxcBȜd5K#}M( =HE`;?`ΐ `J،k)&XFޠ,7nĻ16Bv-@{7)u<Jr׳"s?ڂlN$45d7؃b(@dey.PHr,؎Y'*h ‚?sk5F)uyvF-z9!OfI$STȷCOntBxR S=yR$0ߌf{#y'ިGG[ΔLU FX l6))8'udcu'npL)+끀wiZTD 2Kh#n7B ҼףmҶ+N4k35ɼh-9E 7)qi0Σ{:?i6`òXMdmςG$Dbl%a!O#_),<[ A;f=)BMIUoah$j6/t"o%4ݥw]U"W%3!w=WjR]]h UmN+QhH<7C+PSsVZhl )0AIg=n 2'gRT;"vRqX4M ɽK(MS_'v{A塻 j=oն4ћ8 LzʊQQ䟧#F)['F]!?~DW bR"Tʇx)f 3QBQ>YchHUdm[NEí@-G)[vն&yS/3Ǥ3{KEfQ)X!U!|ݕH7) -lQ2D%!6&0Os*SmFSEh \~/OA^6X%Gg{263.0;B$\'VVz75ΟHCz{lŶ^LųLFmỌ("`eG8 |y|HkbZ~҉6+Sme.9%"4+H HT9jZEwb`.Xˆ6H5R70G!zr H=V+#F-z>YG{a<izLTMYfv2Q 㣀]fJO2jSβQՔ \YyNb<+wi kwF]h\҈~3b%0Ua _07.0[<{:PxwwU-}@FR{R 0gpb>z@V#`L0dغ˕/YN%r@tiH{4cT;D֫1?HZ yף?_:GMM6Y45)_POgStāe= NӔW'$%z*Sd L.@8a+PHxn~2WޤӠ/)J; (J'!\2a@&[H٥I6H _0w F4L0=;*!Ӷ/??۱Tykd'\AZsXɨ8d3qU a/:VgfN-<ҭ 9*>Y3@NYL h?aUgю_.BR,Nr"D56S|; `+v-OMM%MNTWj 崑'>$]SrH93}bcg`|8OiK?к^Z-$eZHiy5<xn2Yx&H,fN6 W7ì8>pwi#;~^z/-J:"?% b>N s _.Zqbd2&HwAG&Gi)wqa Q1ȌI[hTB% *X@_ KQ\FŇO<}?5;V·axP K2f;ȡ1Z dŬ^/M-kb\I3.Ad4nrsBq]@"v w`4tB?'/N])9 5`^m*y4u bJw~P,?EK= $0Wv]}Ckhu[T Xn'3[b1l.h|.cpVKRq)i3.# ȼAbVkNL_Q' ~0L׾)ňWu Il&_&\H\f9zr%I5B8"V0R; :D?^TހIj-R3GTBt Xv̍۲auLXk^jؓa'JYH_I-|iD&:~mc)ml rI[l->/Dr8|a"tgr{@O 9̽8d$UeZn$z괝EfؘM>yW+573k@h,NA7g'.ܭUFYK&Dq n_ΛFdTM;|,b> I`F7ه4BswK%DH{#cHixi]-+8{Γ* Td<IcRlFeM !:3> ܃>b5i?Iaɺc1{F3^#hanK9x*rWk}FLWĜ|N85;E`P\L]J>X)脅U5pzQ 9kpsk(hsN84b3leH-2bPx.[d ' i^2̈ދ'tWK0D M蒎NYŎY}][b2Y-GŸ́bg|\2PX.Ag dtžCnucߌ:~x%qBŴL ׌reڕ5 6ӏYk.δ-AtIxzN(@'L',\x@Eă&ʜr`a;wALf&8$Za\`~ʷdܖ$:qLWeO=Ԑ Lg;7GlD.~Z*2eFٷ͐hu.Y㉯?;Ѷ{V o<#qw rQ@``T|HeA '`ʱ%T;"I.^Q VpwfŽEJ ԛZG4iiĊ`kBFl{س{d>`` 9{Y'W"< Ke;1/!2F]Ȣ1%g/CG wA;Q ơ(J{-*D<hB" m,2&ĕ4` B}ƫjSݨLOic` XǪo(_1*#鰙J^w'Zj;5]Lٔŧwkw%|$Q vbaRr͜uU]8 /xft6 d>GJCZ\sAfI#A|'rEi:?Ü#@mTKdzNcId{T{qYC=;W_Sx"7#Fv?Yo3Hr~K K}[ٜgX܅,RAKD@.yjP'"ȬP<肬49)gGI0E>B)Y{S {W{TiΘ8{ JκV,*qJ.P>BE%@XLywXUP 7vCWpR] sdh1@W@+k p^ePMM ́@ 6p`L\΄Gh7C)%ܹF{L(64n"ĈW=zO} 8ܮI^"qѐ-#. 8Q_o7sg֗3]'m>r33Nnaj"*sPEM]Rƃi"_kvaJ$QXpn#4\Ck3h "&?Ԕ?RIݲAIKw*єE5sEP\hp5njs-W(r`|i\T.]"D9C{yWY@C()c>ҹc+YZhϻ"J7Ÿ=Uy; {.A%K8mD}曯Mׇ!='gk%\ZliG=!7uSɶ <'$<5'T\Z>}l"+>exG86f0lY2Ajhf$4wL*$Oi6VnӽUza^Ƃʇ1.d̘p4>Ml).H]|7®8 n]|>@q ~cmQnB'F}7µ4 YgVZyѲ}oF-|/./]W=bAɀNuׇu.Yqѽػ9:0;=ޒiCAݨ Q*ʢL54МUAcU-҈:@}>1A +ϩ%rf:*;`ݬ9-7g iGr#tUC+w \ҿ_K^i3Y6m">,Y"W[qN6D'6gz8x1afDA>᯸ֿo[:$MiCEOul-F8s6=׍6k-|ǵ eDOz\*J8`>k';HT;sʼQ4k}>Jl9S.ŔP}+CJO؀[AE+ ]W7^VBQ( so(jj,7i?O/hcz? SOf|?QY3^upn`Я#2iϙpx̫`Wz!6-j(.˷^wp $FG2a6AbylzOM0$k3/Eg%R9J_8c7zbº%[a{:5~fekչJ»Lԧ D ݤi{yOjA{Q~N]܏O ut_ɻ\NpC+,Y~>\wgs$r2 |Bq(Xi -C*Hdndt N|!; TrrG!2sC~8}. Ɯ9-[;[I5k0!3 Vec_!3mб;ƛx}lUf[ߥs o9)5|P#/AVl +Ƒ68ًaגrxJ<}@ DyTiM W^=_N1Y'8IH\bɞ>Ef8}9yp,ba}0 ͿW-Qg#.[9C q^F wՎA,)XκvHk^Ѿ-ŵɬV$KЮ}[X7 Tl-JF FFUۓ-1Dd@Py 9Yo@PѠye]`o>h ]*ojn7h5s(]z}IƯj,SdM} kߋ ^.%jWn0 ^߳`ޢYi]{2+n˲!#vzC&'e_81/pCɒ owl^d^G:`(Aa"!4L &D';>qt. `e<Ύ*&e8p.FLQPwPFѾOZd<;"J-T!Fl%YgoRx Y:Waigly0ߋn揻jVA8_-djrT'haG|zS\^,!Z+8>Iv1'O<As^3Żw<{%‚ `p40z6U "9"a(cbtHwMO&7[(-Nc6GF[>v+=P(2% uvȆZ_F,;f;ZtD0cHpn #ނxЂ_E..8"1oV9J9haU%0SSFomSq]3W&/ؾ5M {m6q;x| ?x~+YHIC{.E/ЀL?"ex|erfUοiuӔwfT?uc+ -IѺ}V9wיMn'v[f/GVT ~0 aNazhV w7Zyܹy38b&u,‹m@PpRYA l+$+R*K?uu(% O/צ\hlTp&lwACMtUE8 MFYe?Gp` ^+Z,Q)a xv3ǒM'=]TtU7 %z6Rؐx1Mb7]eIOx KupZz@*{;37[)dz ;p CW(nYvj9[I/XhOBЫ ;eB}3.Pxw܄$,0 67[t[Y:@Lasn\D4-2BƉ +l8̛=+o:?W2X 4,b=~ވ'rpƠE<(WDfwKT2&|Dʇ^B_7niDlFvKFuDT-Bӡ{ףQs 3yK )&y G]lZ0k8~{n@*46`f/lol#JQ%($m!C*?fؒS߽?L|+u {8÷]&\A?(xC]RlF🟵:&^3|X,WC1r^̜<[`Dp-7Z-vLO!{D*% 0G.Dc_\Oj؇Ф]qz~@씥0o E#j*&+"KY}+q-l;>8di:b@/U1"-E o{[;4xF+hRse&Y«US,!C~n"Fئ+YX3bzP8 u!w둏Lg~8t'CJb-)OI]X|P}bMk>9\@f@a+M}\fv+3LAgu%wr`Hg%zjzw]j|#;@!fRlp:_תҥXj|fo'4Q _3χC[>Ux7pdF7 s1<[]Z KsǺ\e_iַt$Ոug\F/OlSZWLJMO]|@'Hx0CHmOĞZu+sh/o|,>|WN'X=`Qt[J\325O57$X X}_{0#'|oWȒ6e|`sma}2*-7Sy S[K'S<\$cKz.XZ\ۖ(0:(HՉb 4;_ +2(3RafX T[.>qҔ@KD;(A>$ DٖdL:Nj{?]b^4א,BC}Mo"+-g1gwjȂ,g>yx\s e9|&1 m)Zlp)G![όd0ԃ1!CX_si'855kuP ٞ1d{G2YȂ u! sX/n~ʌ)f7AZI ]csq&cE3AQ|gdd# a"k[WA)nKǏ ~FT.;L39f6R-FFDq*i>?l|m.XˍL5g2c /%</0rz8Y jWf*өE{k_!% l0n&?}XPG| `)>əkY L;ج8"=̐Ua{86z% ҈xP.hNTQ¸?M?lNZQaD$):11m'Tc}$6=Ex%$BW*iLl] V#i3>ViW5۷h>#(fg.;Pl?{1>_Dq h8xr7>d;؄YEv8etNM 7J'= ±Ͱ閽岂igPYlǃU 27>$mX){Z贱zo <b WϏ_c^-;b]I|lmpQPMGa`Υ@lVm[ ޠOXJ)E"72_@8:<^2vm0fu+^ɫH(:{W*Be\+~*Xʬ]z3&"I~oQ3NQ"o} `M|+$ҵU @}"_S;PB)p8 4l=Sj:'+q =(*8=2^)Ő~")fDP 4Edꦆ"s; yylL4/*.]9pS5jcK[΋:W |Z߽+ϳP5͵`PVyȑ ~|^oV? w_Ɨn.n ̳@,0®dεz4گҎ@E#D I?~tejCIɃrNLhJn5":"%7ktӋ vQ 5-30H,Ph:BȒBC!3tc}ϣ_[ǯRUW{hTWPL22URP]7`ʵ2ϷQv=ڨ8$Q |+{ѻ4[3,4=1\k)rBv>w+葳{ .nk3U QuJNC^\:nKv@pԁG(@}-0@ y*Y ׁဵ{%{.wNzq(&%dÂwdY> S+zi.%ĥ B:'&nvr_=~OUAׯ ^* UW3ۿ;we V؅T _GnAlTRm·/X:2I-a8;̊F 2TNU$,n!`p⬻ʼ6NUv;;kW3y6"\ &RIBA 0z潲ZpaQW~4 srQ ꈀƐN`Od_@_2P/+B,ZDQu/7z YoDӝG|B9ז)ta6W WXcNtY,py0zK?<BF^ RHaz|˲-u:MHcitМ/Oxm}xLf÷ypuOןjP-X~4.\Ҥ|R/:%I9}y87v> I LgH_?3٠`JZ?>qEki&ީAYj\%HAA.W,sI|PyP?\#~ܚaGJb+|TXv6z VzM8\<Gp{s4n~MyFS7;)tl.%Fut4b'3|͟<ѕ}ɒ6f#ƈJTsūvzSm0AtPy2k8`)!L[GIꛦҒS &HmH> uvRg5}  Z_r[2` M31:-uJДAM9s84 ]T "P%MCZK(n,zN/4LNP8_Dy _S?C'֞1uCa P, Saf=H*cѓr[XbսIYQG@]fp0L"Mie]ν`P QAd.4DJw\%oޘqWuwhЕ8q%!]OV@z{\4XS0%SwJ8Ds9).K.B )dĥdSeW=l_BǷ$axa=A!c-`>%!v }r֥Hi`J=m^1ʹ d=ͩ7i&4jͯ, \IʣEL\Ԑ'L-6E p=rW@U_p{79HèMq9p:HLClbYģA: 0o*wLQPi`><3t=R[e(7O1Ư~A;Ofq\`3rzJ7*S62^qRPA;** 5yePweժ6iH,6V]7\^jek@C Ly-HoHIwǪ틘\pQO)& =c`BFڣPG3 q9(ܰΟۃJH퓫0P`ƞ2B9%'*¼#jxl#2 TeiA?+E6JǟVx̄ʒ.'/S.87SvU:Pk6sRJ S~ BsBjwy6p:xgGdNɣiOyNST&>MsٍN=L1߼V]Mڭs?b|_4IR' }iiqv|jA+7L $۸Faץze\"{ѝ&bt+|lEgzSce"2\,o;T /뉕û3̭!*Mb4DzgJ~LCo!N@W.|q B}-s8Zh3&YZ2&I I@WcY5 1~:nF-ǍDQCgSB>wڌROSujoSů :9~oQjm7r{EAPC1ִ!b/vZ)Gs}h鱯6M[I羞8pl7^_ ^7RNU1Wl̖trJٻKwUC2/D fY)SBGDo֚۔73v0=]!凗I3ȝ"u.`^iok!qm 7 ! f5 R@K%UL]{.cqb?8g+:&~@k*zp@f =ԶB`FGUrOc2UOp&tA ćz{JCLΕє,Apt_1$5|a{a]gf8nB o0^2q8%ĬY@IkЁZut*wu GBќhpkxW=vx)n9Ako0V]fxsMBc·2c:Q f.Nl^kBr9 t.љm2?(>e(7پܨ#Jl룫8):# ^VtY#A]O靑,اvNbEg9p,V)033hin喻=8xu]]r."q E@+tfnS|ۖܬoɔ?mwo/.QPC0#0nq߅*a,k)C`۪{(u%C g ݍ=_L;Vw2?Vք.5"oxzvAGYTGd-?m#^ Q_i[O+n:a,w0Tk=R!{G 0 dSiqҜ~$u5mDL+ǹ)>eap)͸ e oMK͞;gj_^&|qwK z-رe]/>Ӥ6]W+vy?ܚtbSo!b] H゗6"JA@g+)΄Xt!g&L^pg Q7ڃFj}W(f3~`@B9La|d+RF/}Op>g,pyb?!+BL+h#SJŪ_AKDZy]Y?H|O5T2WYq ۑJBk߆mUpcdLgpOkCsh̐( f@\<Hpk Ź~ ;Gi+H\ P3!zdsUJ[sN=ܳeXYٟd QЃX&˶`8ws>]q[%cvV1mSc*5nq_+/u>W#h aUg L65!VCra< `6Cc=Ӄtd,YXN_ҭ1Q>!gAJ(kR ck?D_%?Uu' ͫBm, ܮ7m`v:߿;pi, Q`1GMbV$ }QEJ {3kXtEh 'YM`FyY$U>%fxC\G!C*JT>̰0aRO𽼒{%=:OOsܖ)z(O,i).汵E*y8yI7 Ewh1vSv q٦ZBQ^j5Pੴ}rhRa AhsZμe^Eظ'<څ>$\m;n"Dž5N $9F(C}kb7UX4re ΨJ0rm7 HWqr"%1l0{ 8jB,Ew5\7m#;7gx]2/J̣d$2CFb\he$]gvqmH ? uJ0S@cj$n[%*X{ g%K w,m0Jmâ}~?74I\ٓ*H]LAhМ>8@)<|.-Ǯ1cg&i7]cdx@4Y9/8dz'[Moi O}MG(uF4G!L˔#R-FꏝRISgVJWHZ(9ڕk^`PnƆKNYWRFUqrK|;-.u{Yz JĺQWvIώERi.i(~Zh@?y8 ĶPV)p2 $V+Ұ*?JؿwCF0aW~^̫CrX'X3zKbu?_'<#d`uuJU6YK.@s]F!KQ=L5T֏řrL4E6gg^}|á _-qHbuE aFKtdW 54,~6(lN7p+#ASͽ & La B/ۧ"s0T _uP 篤 t>3!BLc*23zfZ/$Q)*,yb!9wD>.?bvDR/1~gA+~nl->ȩA3 M-1h-p@! 4^]Ʊ&c`뒺Y*K-hDI!'+ޥQgD\R#u;EZُϛl~YH't~I=ů椋'6?~%ipZ]@*YUTpRV1#zHR{-v6R{hz̕'2mRт }"?`0XSԤ^\7G,ZU&Dӣ[/WIϻd/8kPT'j2=@ޫuZJ!_, LO@)Q䣹+69wQTJwyٛPgMȩ'k K2oES8C%{ . cR\xFv%m{3<6c\Zs[jT[cϖYX?>/9&KRXBIP/CaɧPx~tS-xɑF8 %2^kCƸnCVўcGbQEhfql/_]K^F'O-VSn *|ɱv LEA?&Oz?r'ݤ/GMօ G*KSnt̖]2Vz7nq |O;RcꡙAs!8qQ8ʖ)$m(]@MF@Bn8e$XPH(4 {ަx|.*'荘qRrqBRL)ɛ28c4ܯQUr+69@ё5G4D&-D+uLw krwevgb=Q%;m9̊z]cx c<.fF5-8KAN2S .KQs;_y!_ ib#Kqs y Qei!+O5IA"_SUTr( oVzPO??y-nZ7Ǔ[u%Եp܁;SdkǽFr0x6iK>?3"z'ɖ'#qVQ#2Ϻ/1%c~ طl›]DD/j mQUFadUKlӽZ[ Wtnb˳e!"u.bԄd5w:ӯ7,=Z1ݦݪs??ɣ) iH%w߬rԒntܗ#{_m{ZyOIt#7p($@={D5/R^nBbdp>7D ӜTNt%VwnlGDi$ofZBG`;;v$uysci>iN4⎜=g4G ?+M11X#?wqosJn[xt3pA m"Q#c[< ϛ% MtqaH\dk 9ϒkX$G/ҴL5W"y[IgF9n D477lRIKcX76tH91a@ە%/5& } ?7 HQ?KE'ƽqЈ (CZ&8.Wt&,`k5TG/d +u9xl#PBM3;21^?Ǡhv_yo)[S>` @9h>>4L/_?٣T~wJqbW u(@%ɴ/>ls LޜmwC9͜l{aԧ f戲MJ5Vrp>e/T!6? ǭd&۞RV*Fb?ܫb%HQŶ k1{ܝYxWeǂ ܎t)|jN]Z 8Z_`yNo&tp.t27 ֍/=-1zb#Iw!|p@G">~1@ 3I 0؀V oJGz ?>lHwξ&BOt6!ߺZ]k$|r{X5NQJ9ft9bamp=󙌇>a =;r|˭WpSTy{ eUꮻO6I T٘\ X0/6ҿ_A4bh%epC#t i(,DvCJ4,3}mz Ljs_[\"CJ6e,4)y)p{S@pt3pqbx 7tc}O[LfɩM:=l yjNv5(MU,);Sc^t h W|y:gU[6k{U<\UMsG3k>Ȇ{5AؑM2߸YJ"V*ʔImIوWt"`/Q%h~F,iU,:o߸cEn_*!UNbz^yRIeB/Ύ>km#ÔHk>=+XP\~ΑN_]g(˝uEZJD[Mذ8Ղp&Oz%VX 㝬/vPN ݹ¶X0%=̍$KG^D͂cl32qS>DdHܿQ#7+a7܂ : *\G o=!W1"Y,%gdֺR 摱pIqH8cV\3/?npj9\0oiJKj}M*U񼮰/L(Ev)Q.봒V@Ɵm&Pzpcy/E]'5D)#D *=R|> tM J!w?u'r .%Ԛ@( M$6!>&HA`<`WN!yUBI4|!`Zxx Rmz6,F8d y6 -jca$."W#s gB]CJXIk|>Mj'&L{&ˬI0zJ`? XVE;Lԛ7Wm]y4錘S~IE& 3x8vJ'|\ ٸC19(=Q*Nk㳬-Ha4&$/AOQ2a=dnCٌ4J0!(LC f^YMsBɷ[fV0\"iHSII%һ4>4˩ڞ4ݫ = Ry1"XrLdv*v Y6QG3;u{rV`m^VWⶻ01B9m/%{#d4 Zc~07`ħ:3vp'8; DJ&n>-82b>mxagKzE(]@4mlT]Fj:B\ӦY$磈h(v'xd3 6P?-u ^%!0(!gh*ynV߾Jl"&P? Ah&{ݾEO.?!O36b;$M9HJ(КhaIm˞T@#Z XmS<h.EҷQYK)&#H׉QeЦJLUg_E5/O~ Es 7c֮% gHV~ ҝqOBYzbA[_\x Ih sJ[j6 ֜G.uDhQ(Աlh]#XR gT9*%+lgp+x82sD^@\m ,Y^'ph*jBX3|vg5Bb@X(w\+r*1W)$!f\4_@{0b4Ŕ62z\WQv A.uM$WcftħokŦ0fjs^ ßR[)hAVrrOohԝ}+ogt,ȚX1<׹pCves~ XHH'`t P[(t1TPf?eCIzO* ђ" VZ-e}YAmxX}9%p ,s9|X%80%L=5G-h3m@;%Y VM$9$OlghƤp }CtN/*umur]*"/7)=MrQdR+ `H;[y3ܭ Wo| 'u xɈVqQV܁)J71d< [YL^9@bێ/y+Ɉzp<xפI>4\|7H _:*90!ZhYCiO"yL:iQad7@C*q4dj~3RAyo+mEL͞ CPNe XT]Z)=8_ meY8}0ql{P뺁r6 .=;$Z|*09tfIќ֖w'i'D1hq"" ui-lwQ SCϼWsɵ(8}*h$vF|DUѝq|ôNLq1 g6.TD (A: x7 yp# -~AU ,10=Oj{殿Ь LHyE"2H_>t2AT6e(;>Ȩ%P mR.xC(*wk pNHףּ`gmD䋚 DB]w;Jcur ,T)C)Me-6ZǞY@'f^B D6Ɓ%;؎X2VXKK oڱ#>@*tJɩ)߈{<kF(Oܥ?C{z sR?F!A'.# H]DW$\yϏ(XEu'hSIqqlQ 68z_N 8ɯv 1ʪ9aV>A{gn!&>>s-.nTđ)M[).؆b]:?ֱR|7åJ銘 'J|ӪχbY@/3.R g]L ՑPY} NY#í:/5hS쳣Ѻ a?χcQcRؠ-m_sԧx1׮AJLN m$z`n۬Ws=ӕ#)T#a1gdSqVg!rF">8${0 @ HTԛ~zU]*Ln/lVU wB9bz.o80rni14h `udZtTzG -՘`? [)mm,:v rk!|h 0$!7;1T4Z=ƪWhҐfuVLL\RУH~j]*׫6Z1r@ܐ@`2J*ԡyWCQ?%Iw6*e&:GEr[[ K ?VE@6jcp'Oܖ;b $ w)tV[Z?j `~`+!M/\,O-zi3ǗctFx<8Tic>NU}guvPXnx9ș/EHq'ˤ*I:HhHA{n&ɝͳe] pKZyK%ٽŠ닭2m69gx4'eL$c䮎wiԪl?<~1NB =*M=dջJ\}6!>6exPR\ٳ]$wK" :NY.5+YІ2"ajaq] Ja.Po<ƩS3Eu: XcEɔ¹N؅(/,r/e̬}<-[@ݳ o vx .22.*I`wIN5]P'zŅ HUF::E;+TN)bqB?vtn6%ۄ0 <-=SE'GJB9c鬯ٻŀʖ'_A8bA+<~ '3Voy=G)q3'$h%_iEnmé/ւZ( ً4x$Ƚ[pF1Ȩ|#"d!Q^*xa2/ $h q=O9 -xܥ[Ym9L',=M܎蟩z/ӛ[* qK 6 3#Mf YR'Ϣ[0^;:)\lyƼNn[L4 VVrW2i.~s>S`Si.[l/;""7ĥLu0R#] CQNVx H,=XLsq p*0"o6xc%=:fz~ﱂ\y.`BO6|sWu6 WMwDVcgUw8s/~G P[`7TW H:p;LuT ck,i+ɠx%Ɖ'}3IGR O`ók #?uuqjo5j:N f LcchJ_Mz0vLzՠ'Lb~S9Y%vYB"Y'F_z#0~f nHhS)I=Ig30=ޗ(10bEZ^ ODiUPQA(4ƿ!9EjFFϾNI+7GɗSgc+Ws[H\O"iqX{gч;Huw\~pCb$f:ySq+\_F}w7* т,+Sñ䅋7iS˸Wa)k8߹ϿX~f֯"IFFl^\<;]pB34d^" #[#ʡ$̉gd\-_̺e+qջY~#\B?Q†1G}% AO%d<3Oi(piAS&m9 !=knX-Rm Km~f3n ݵ(ܨU+{L hѪ +0hH]Qn!+vuF ɃG,]5URBEW YocT;J! '=-B?vCJB`4L@ZsM ?=R3J)4YLQqb+̽YZ'vZs3u].lݏ-jڧ5k]dEꯈV{_"LlWkH?PwL?P%E"T}gBWjQ\lu{r\ßz83Fiĩ`?X/$'֜R,; ?\&:U{嵀,JZ%n9}j/!NќvҶ.Z~O#AF3:WeFU$&tTVf0}K8 !5`l )ȧ8W=hFnlߦON->@Tb+.Nc#R2m9 MKeRAuVqD炭TG<#M:ۀ_^k?{ӈ10$m3iDr$ :9)wOg;Jـt{F8Uآms0SvYS1 :+d˞y8AqUz4-ВÂd.>8 [o.{szDŽcdo[mq'G\,t'MV }1nT{ :EH{O̟']J#- ʂm4 t3<؊#-Q `ɌVF֋ԘҭSQibzfCTzh#5@]^,~ҙAξA/-RN\:3*b`F-xu=ϲf/Zfh\9H eZKW;I9sҐ6\t ƹ[G.Ks_4PCLzYZq)l3u s hiQjՅ)3B+ƶ.rGf`xvi8I[53ym²1PmP6^RV3PvOFȫ*U.,:;7|YZϽs֪sn %3Gt ۆ14R5 w@#׶[5-ޡѦ=71'͗5D^'EBi|؄LL w CeY6\fU7~^-.*+ hnp.%e_ti6z?3CTﺽ9s}&<@䡘$?8 * JtJh]8ZZrJg~IEAϣR݌Mom1PErJtbq;?gP7a}lU9U1$kI;Ef0&H׫%H']pH`.qy?p# ]֤l@&"#cwf;/H/^ :_ LmI Ys3-nLn+UZ~ؚb`T@`jcb.SB{FZ҈n ПY]Zj F(-]~BZ[pv'AY(^72vuN<Qq"!D#uJީ3A_c[ Wae 3DgP2݄mZ$1~5u} mzF~& 28LXKF2;~dfX74M :Щ2Qard /ߋ <9˖z$:@eP/ ؝cbt! ?ٞ$'qĚҰoiEW_<W$r\`˖.Kr5!cWŨ7+8 s_ݚY%*xBmǹQV*bA/B87|݆ȡWptz18xe)0`kwcMx_'oQ#8K֯S&B.R.5ZኞV*.wYWN#Lx,Ch^dd[7{[5uY,tM' 0HQ?B8 |X-FӋ ٣2y 7Ul=mN߃)5q]KJm!`k'tGxƌ PRpc3v"lx ZUlƜLK b ɰ<?5:RRLnMǰd,^YcG[~R(OH9p+EreJ[Gq %F F6Jq\(?{Bl+6F 9mөdu8 C!FvZf9Ƭґ }N3?f Yi B` FkRt> \.e@oY6g9*dkQT hE-R[3!1eqe@ӓ2?71(zC0,tkaZ3Ǡ]p)SV@ҘSA x!O(=EL4>IՐ&8 cND}K+wRtd\S"D17w\:65,:W Yjs88VSDb?M<HC@wnOizN6kFQ9KnLM'//٬\q>SG~+L*`OGe;32lD_\Um@HTJQ{rEj&ߝ9V=8VD~[,lAw%p-mrw3|;1fc| bni]H|i5ӾKoC":I}GN)TV\U&_êIXJdwF7,vT2w 2f9e^i[M?u$Lj CbgEBw2n)f`!>G,1Ǫ}jwC3帝˓>*xה7p&̕Yw-Jg5|IRkH]=yn\/=V\BNlv4'34sXSg@q>kS!^~ d[8i^^trvT u\ٝ2p؍ћJ8+STdGNe¶Z i~ϊw|0}?鶆ogb,dbw''Oco9-_`,:VXR.'t8}EP=}*I!YUKZ,:62|]%zĸp}fj0HP-*-7P nH>KY-N6$KEs@ڑF1nJrFqR| GX9bC{֤8u, X; d[P kXa TH &^>ܺ&-m'cuxbCu IzyRiv]6*5(Vfh$+&d5\",|`܀u_`6| f2-YA|7xNm{s$Ӳ$E)%haK@r-]yĩU̦ttm; Tj]qs`n5O@RO&ewŀAYV}.xcnrwh9lBjp ԀU ɣ Bc T x03ܖ8vqtyGm{hHHu_wS**$)/)o5ǵA$Z$HQf }Zd}if1E.EmF萮ApJ͕<ȇi "`35@osC\CAQ`%PM6X$ZiӳHQ#(wKd`z26{߿5Cbn* GחbiPMM:X/bg]0ۊmFLfk.Y&$m* 6]dJ̹ZPKƸV7 ȼ4[w]o5}cj0ok1FJRQrjv{f"Dsxi]XޞafNp ~'KdqAlje$ӺS(Uz [zcZ 0h%V-C~&e FE7^6q=ʣTpo//~x|;5Ap38%8/+*iH@ŷNȅ-*- fb SD߇vu<܉ oz2WڀZvo9PԳ`˧,[y,zl Hyzj?ܠ˝`,ጒ,ȱrt |{"y@+5bs}`dZX3P&qFѴƚD 'R-(JQW:oއeQER/˿}QRژpmϒGQ}&hF3`yr4ZCnڻagT0z?5ka-9TlOtrz)% A5;Ύn "&93X ޫd>:bFvoPOo(&o`F#! i4XV!q)\i/kgmZzwAa64`y׸,ťiҭ_]ZM4Z-x1_V4'*=/%{,KE8WjVL'gCƳ ^#}'qYH{d:OsMJ͝T*}8-i89Cf*jĩZ_Ք_iV4 u}j"0y[⻿@kfE[$!5\udrPGu Ju;ǫA}7$lڠdL`)/;3x!3,Oa0b,D֟F--U']dJl ֥#7;+HRS#6}Д/MQ&KTӓfHI%ʓ-UtX;& _ެo82c}(!hJu[17trƠh5y+p]V (ƪ!0sZlLSSD^bF#mjiHW=BI1bbe7'Qٖ DZo4! 9{%y;.(A20י6X,Y3L}9y~$e]/'AVOJZ_vSaO-3hXt\15¯iMugjz e?EMY@y;߶#eGO9uz0#@ØZawF SLQEfŎ1kpp]/~ ~wM6Αgs5F#yב{'ww/EqWh8}h SvI /c[gs~]E+1'l 0K#񔐞Z~[>MR) FY}פAmhOQu.I lׂ Lھ:uo;&ՒGν)\*@]p)6.?C0G,܌ !_~ո0q[V ` \qw\=V"ޑ4 ?ޛ\XZRAj_xmCxP熦]>kT $8Z*u#%?\#o HTOA%[7l䂴TXMʬYOςEEqtF :ّj. U?)pv8mX˸liI|գ]՞"-foIEe%YԔ9ZNjN0c1JW`I$r<;Tz דYűGũ|@"z! $*J<[x2e'eMՎ!ENVbed hP-85p?~Vac#?$m}~햂OiL`-qC:Lj0yy6XӦ"ԝ؊vNj}&2r8h;fkʃU*Ԝا6}p0 k?UE/ ڈ_5gmwqE;}u6G?E%+BOl7173'̟=nr4RD1$%~*P kf WTP}gpl $INZϲBٱt &;0>ySl/ؒIܭ4䁦:B-~Ltpq 'B'_2I$[W'HK;.MzOXl lqo]mv(](wKo 08rPys";2bbW 5RXM$ @NLmF6..)<~p7hvY`EB)iXHƀO>6 2~3*eAGygֈ6U RXmґ%Nu*;X>sE`t4c !$A'27v主B5иC/pU0V'1D0^> m%Шä}U&pQlzI=Bz&x8> [? j6[Ɔ;kg-kPs R]R0@Q4yKZR&Xn]f^tV2tuW V2!6Gm<] }MY]Ԝ`$n `?„b >6~eTܢfmtg2(Ɯ^/v  DuAs ,z@!yݑ?2 qhX(M{ n<(ݧ0u 8.*t>ULE,ܦVa}ӛ R=, ՗gf" y OBuV ' a$o Hd)y M<ERfizom6H?ΧJ;Xث}Hhb[.|P W->'j k*~O.%9Dmi5<6<>ygp4f;X:]AB4 ]?Xb_nw"dNmM3 >Зk= Czdӻő@]Աګ-NIw<7nX &Hn&Im9/M8l9JIQXXS$ݜ̛h8FmU rڤʧ:o%^HaU3uWS7! ax78Qlsb=;!LԐ\ jaOgp,BxҼAʘеԬl*>f(QO&q͞U(ƶ/V;Dե/ y}ZabyRQJjneV./W7oRBek}=-3 HgwǩޓN y gƸT<0'*I6-x7?T:zx-C]e_"54 #Aq]܌n\ +m1l L$ѣse11yxQ97T lֲˉoSlhjŤ8ƍz`=uNӹP)|ik)/n֠aJEz^!!ê7,(BVCOWdXu_9CnS]}Vt˗1˕I5i Xj0,JbHĄke]eF|{܁Dit*'BMt_cpCd˻+[ 57aGbGbpG;$)}-Gqd>,sݛauܣ,},Ŭ"zlrwKQ-)qzDR1bn6^Y}KuhFHv< 7X@1oPlv#](DVulH*xZ~f7i4 9ua(" ,Ȏ{'ttc!VW~_@XgOۢS?}45I!p:b,jx5ڇ7+"5"/7d~<FV:MP>W EryU2V5 m߳E<_+;+EJɷkU Gm- 2 ݹ;k6N(,"e&/zE.0\ٍ}vx3QD9e MG'8ZQw\EQC}Wm[t ) M#RJMӤuWȞ )^^E["XL亁'+a_Q~t8e('C@?b_5#ў"Kx #V6tvw'ǑRUkrQg{ }ǵU@];pBpSGgK],Bdu`h =351b9wMU䶅\Ff %JfQs2| DXiF#[YynxQJZbK࠶Ch`&{0G=[peTgR&A"Tv.r"o.ݘ&$3~qr1xTD3{>QlSƔ5iŎ #f{})2Ve ?k9z7&6=͓h9p;j!\is$ok~j$ZCQb{kNPv 6iPZa$q5:9{>/!Oοo ֠yPe!YH gHGQgmJ܏LMYT :+4\ؔL0m9XwqQˑ{%*ɞ ywRtI{PpEPbz9X=VPO{"<}^j\P<'DŽQ3h-Jk67$s,  WmLy4JjI 30F) DlM?Z3 1E([ #N`1b7G\1+լ)*R1a0ҖΒWflj1Z31졔:= рte8=iv5zsC8|a05o~'z>Ve8EEw6މRr,a4ƂB?CJCA1-wSS]8%GU0a l32mj.pHr9:7cbeGN- _q B8[E,s;~LFOpjﮥ8O2eP:G =dЯ(8OsV,-֍:UIA;}@W @m`ɷFrVw4+[(Tb;-OCq, Y2=V)V]"ADdWt4MB FI+( T]3uiҡ'42 H4@SVW5h=RvY<(ǷX~ndar;&iqU"1G '+;ӠFC*ȑA"+7V޵y&XǷ-wgDz@\Ƨ(0I[ OI8#y#q_xu4=HU t- V#%s/ 'f4Ky=˽Ѓ0a26 6i.)˛yR(GijENdw]Lhȱj*0g_Dۀ qV ,{q~SUB|4BREE"<ԙ R_ YY *@ r'^Pc^il؈(V"L! %- n?%e=xD.43-}݅"%EP<C: ~$ >P;ە|zj0fl _HY%FY5Ent(SnUPf) ")wEZC &_JϲiAϧ ztYZx+l ZSmKshOH@å[ "2ɄBُL sQ%1`k&}cDLǢ5X"L貯_ab%H3/<~QQMQ, &]e胅\Fu5ٛ`01^PklSF'00y2L=f5S~c˧ YL{o]ND&@<&ISw9k.Ŵ|0(ԹoϺ&w g+.I,-`6*<RcUZ9]utFvoFfA+;\0Sݍo0WG*gvYH?v.w]b dɘ-K& xc/mTʷpV" rdsP~oq:d?'.3`1BY25]Ey{ ^1> Nk .ʼ#Z>B;Tt<ȣ LX( 8V4%_:dKyJ"XZ;I 0촬@P斪 &h59* ϐELlo'a$}ĥ> gî#kų+WwazfVdӱ!Ir6~7cf!jpӢRLSM<]NZ`i[DVtaMT>"U ^nS;}a縷PQYu=-zqEq@؞')X{Yrq (/?e{=]Ĭoz_K|bk 8S«ehX1dV(Zxn>.Jr'x*8*?M 3m$eh*)|gEG'L"+%i-꙳+Pq(twűIBmJlen'<ҧiEpkv5:G,&LO pXExو2?fODLTm=\ZrN% s}njo06XՊ FAfcu_[$VMNPm* $ *ԏ5 ]6gW'`?8Qܔo<+`D T],,YSRw\0tHekwBE (.vAwyL^zl!)Eڦ91o-v(8qK6 .4ߵtyR~4i'rW:PLFІ4ywV=(`Mv~qK$ [*5Yu> SX\ R+ z!Acb}.Y 6wA@`8 F f֏<}eg`Jb,3SPO'.,J1A~m#QMB1tw6E9fV&F.{.BcbQn>]p5W4$gmzUgWQsDQel)@%2@  `DGKf+7 Bg;^,@|7C8 oC8 [g^0<0A> pRLK8 Hݎ"z*BMcFM|J8IJ$[i+a[j3UVJ0E複FbvVJvHKvj@y褸:|Pk, syT쓏U=U}"Oo¬V8 a'15˳&dg#2]nJX ȇ~!_b4CqD DžU&] ydoU*S^r]#F<33kimC,맗NKvk[ow0}A ZacFՊlef |ːL:d&8Tc\d*uݼ߼|qpUdkv1d%) O2#=7 {gոv Hz2!Ɛ.дu-ɺLnɃ CQv`# + nU|U* %h@ u,uv8 >Yf%T?*Bs\?x*, K8L!>h e*H%0?=۶"KӁuk$&Mtm+JJv{ K3?Հ%.׺5oDcuӹL5}Q> jo?sܱTM`^=mO p:[T'$6=]L^yXU/W~"KK;TbB 4E"d5?M9𗼯Jer!Ǻ^~yG\ 9eM67Xj~9%ѱ`BwA2b60?0Dzqn3숇#!9읬̵X:DzGs0>jsMrL#MO¯;*ij: yk;G89rB#. J5 y6ć% b cyCBÑWL}o1ly]t|{ݓ_)BZv cP*x4|\9/tӘcp\iLr J6y܄n9!(4F7 %kZB&4Zgxo^!NwGo} ^x3 h` 58fV+vdnIgō RsZ [jyb+;fs -w t]>2VcգW;HSh&кD|ɦ/kx,&YwY(J#HֽD"CoB+su0kB+WL#EnFM$mePlnD,V(E -յ6QZcLW(=jm|c6&ïv7eߘ?9r%ʞD9eiL=ep[nZeW5%G2V7vj! exh@PLpWz4waImu @)͋ ^a(x[)+-T&0ZDv/HҐԡY\juPzDYwM e{ʛ~s5H?I t%nٖ$h?@ƫr)oՑaYhoٌWΠvۼ2@޷;/|0>d8Aw&3T ڲsNN 1ئA^g+M9NS}o6AlpxDj};<Ғc@-Ĺ˹tO(G0&X]_CL.MƹD{朦`Ǫ+7ӓqp?n2:LjE9Jm,"ZTJ4{b2I߷$q7-(OJ5ݱ%V/7~#CJ!O9^.A#𛟕ǂ9 i}O mz[yJt>w&glm$Źr:bҗүٲuIȟJTr~gnH9[[ZqݣK>"X112bc9Oji L_/slmBdVAG`މ%G(BcV951,?lr=V>mFk/1m5%3I[C| @|>wfȸ+ *>Vz =A]-qh城`hʵˢ<p 4N5&I[ ms˚Tn # &Xfioay aʂiR&j&>BVGsk70N;bĭ!0+"xY 4@mG}d>X?}w" k#)-!/ P?t*1UkKGYu@: v۹g4)ָmfPEA[T08xB"Nta Ǟ&[?w{mi(cs(7m%h5}\۾˼$}ǂ/_|I 4& W -+@Z$g5sHJHh4LB^g'VV6xUF>241ZUD~nVB*C@TS_XȞ V'bֳ+_ޡKv݁էG`34h< OGo Eue%#:ws#A"9D=c̥Y;@ab!9~[g)kGD hY ʰ̵(mdגeVkn LZNK0:YpzL˟HmEk+4}*h:-]q2T(zoP5f9gwEf"dS"Wcc%1_ci"e'ʼnv?sax ׭R$u}iZ_pS o9̡f]H`9FFsE*bgb@SW_>^)_!_$  {o%> j+=hRuSgF <1 ҇;Cxhixϸ.L3TJ\ P{7+݇I'[.;oaq2$'X$͑,."{k!#'F< e-[_Yy кy/ |ڲQJn:9cq[T*Q,䐻E(AL:dN# yvijwRf V"\d+oW::Nau!Xݽ>0r_ T.u Gh=QZq}emDiԹmcJ^j8DG17Yu-.ޡ[0dLS%/sec7+7;AK8!1. *lRbɶo5Th 3mR^"@S08=` 4= ]?ƠxXVÕJ$I9X!S输 u!]IոM;l" ohw].j{7.jKBj5n 46)T_>. Ǧ;蜗uPx"vINn+5ieN)-)҉uPHYv7SHM6X]']܊<[72]2cx̌m?X傒 b>4kU`W5+wz#; Q 5=E)y9; eK؄p`+H} z&"X\Rٌ>J\,?]A v` 񭚴HpZ n6aÚ (]F߮b81# <~!7`q.>>尹ZY2+>IGiEso]@pVurF)%d;w 'NØ>g -?nJiQ\a%NG oّfAge~{ܪ]ꪭs0121؆ }eR-U5y`nӍ ~qg3EڹcB[]h[vus$%yk-+,[KA?-n\S$Tx.z[uB4Ѧ{dFV,&HBdtwyP0j4xm\U"gm]ܛ}[vR>ܪxY+rIt\.{ ۪^H?6ˏ' Y)!+ k*=l-{9j= <Όv{a6GMQo|4,Hḻb7R_5%>E2m>T8u|r9T B-U9f!t3.6-M) 2<֏CZf'kG ZjNa;+P`U B8i`e o]_VY !6Tw²;/V Rq6Wx*(ۅ<j`~EiYtCXyૠZxw\r*ovYB[~M)ƙ+*ހtt"c"T@njC{N=hIGH!vլ.'+??[0%1:}/s~a$aV}CI 5~2tH_ٿ @p T' oIYNr+Įq֖YH .\y{c>-O]^Q-:/U? &!T+ذ[q&}F\Oa9RjNg\E`K=VPHoG"$S,4`͆-u@i[T,Ysipz~I<5,PYX!U]D2i5ωS2&q]> Vp@NX'jDòx`D2wY[S:"W.nDoZ;YJ5h:R/0Bl = sOFtKHz7C'XpY? QOó1FM_j;܅&ؐz0V9809}\ev!̳cF Oف;q?ue cQl@^IB(Unߓ&@wt1˦бp(&/>۴?UBm_81ަI֌7.B =a,]k}.y=8_VV:K "/|Y53(P9 bLg&i҈-ř^lpUG/"J$ᑚrrR5So[-BFq S$lIK}q3U9tVxj'h/I6.CܝPIwqZ$ؕcf*7pYr`T8v8gkζ9A{4Κ`¼B[l[0dsfP,D0^1x?LYE`|.T |$Andf_~y4< hlcv<5^b$ZRNJ`uɝ{Up=Im҂_+RWYjEv>ؒѳtmR -QpEz~6}Om;X*T%cex\lG݇ mN{5ƙ\a3LKѤPqm;85t{8ؚ"?g>~HwW&lِXzvPUa׌$a [ģGUXW2%S?ѧ6G>Cab ϿMd<>rIg|*ޭkҝsYh{hLR?f$%#CuSlBia*esrA *6x]t x} \U(Ro®.nCBG ieV㛥Dp:4HҊ d RF A+p37FW* 3 g.? M9Dy-{xʁ+ 2yK/vm~1e}ilu j٧@f0x0\5ɟ0@xn'}w# "F ˶<Ǩ5UKsu)y1lRV$ _JnơDDYEކ)1Hb}ٲf$l'lҙ.7Z :-HzG6P8jۙ ]2B ">4-pQBn8:T/0x}^"m_0r ^vd9%GwBڱz@RI+w2?m&V)Z)`VRO%TT[UJ!u|[HU4&tiҮ_`ݼzc|uVc5"mu Ч0"U^Tc-eOStSnB3ߦ@hGm #hm} D,8e *^x|VjQ#3DM wQx;E8gX%7+Ye7,4+uo-ue=A%]~#.oB2Q-#mo8w!K^;(/[| +3+5ȇt͙SD@:`QEk*2rciMj,HO 6;!xnh)F#Ǔ5s5_H.hon&$ޱ0jL /@|R(J=s*pPJFҌwNCLܗ)}:|#0𽊶ަ([PUirI?;|V"(4x ҏ,*;CQ\[΁AWp& /l8 (3yM kފma^.@Zt(9@g[Q|c1P"Y߳ SlSD3Ѷg~ֽyw<ıLI3~u!Ձ"5H# ޑܨmB24ط IgKC˅iߒ_h8o5W;ry%G$ V7 ܺJ}٣s}}юMNO9=LMKc?YPN!SH@^ה؃f l1RkO!/ i7ӟE% '$'o>fPIA?%*~C81]-S0'[bL9}t?Kh Mq{T&BW7 Ez篵+EkM%;aI?H}9p\A"9{lqGC'\ C.B04utm{))c5a='!S_jI(\M﷢ZR:pҙV )NJ@&V5ч_u֏$-Q16DPzﯟ9t0 {6Z!)o?ҵ{xn뷚4-U{fCH09^Tgg@C?wp28iw|c0 YVv&ok"@?À3ᤄJt|(PV{hϡ=)q-.fGc83TkzO@*(x7)D.썞`Ÿ]qj"j`b]f!UaÝg* =WcsL:;QF涫;47M-[ RPؿ{ S_}$ahL2okbfU^bH٣e;=0CHw;iNsbW `z!9Gs';.R⍀׃ɣ f-xVsjxPܡ>CG J\e*f!Aaɡ=ПKRnl gAvj)/C2JrH`띘k> Izk>4~&Ehm30n,!w/A`:'wP'jתCV̲WpQdft ;BO:5+#o$}-8OA\} ynU3 MQ2ĐdK,ZX8bMJФt/h 9AܬGw(y.hس44SO(ݾ+c+4mS(tzbɞ7-I%]uwAup` )*$KMA~SںG.t{KM `QzҲ3L:,@)gI\R1|d^5X]ge=P-,O #5j0{[(qX xm77@֞},+PTRӝ2wO1RAV2MZ2Jת1AI[9Yrw'֡jhP$$rK&(oR:E1OlkznS0l|Gx~ެӸ4`apZq_U@).r{i%gO(fbc4NO3YvC0R7yUZdEGdB EMz" ,Z=BNTH6bJ )/ZQ YWov1&@\,{M&k9a?ZP _}'2 ڕںdzϩhkԧ_j/o.ߠfN,qA ^e/a#t_`KsiCe`d9^ v05kWqaHngFd;='jo,jD$afAo > QKJm{ިrA(Q4{]&AjQ)_0teM<vmk(샫M+4N7·gw0Ibk@4g{4E ko18@bc'Uk/8B," [${Z/{f HrWi ,Q&0TbNH&r  Dp=Amrbde~F''7{Q*{!zҶPҪ?Cpl\fUx敇~}r,(``m}Bm%uMBoޟP+fKF6Ԋ/E59%klLeEL B[:0e]Uc2+T?uzJ&0LdgR9Klh(6r!o~ (g[ 2*pp]Mj,Քi].)>KNs7۹ecImKG$=p! +5kX WCtыeYncvً5ޙpP;vA98+apFbۃ,La{cWd)#^43: O=frxҵExt..諅 #w5Zc#i%wϼ):~F4;ĉЊ0\s) YO>Kx-Bj{Jꭜ}hJM5NK6UQ>st]<^rs9}y}z)|tBm})Jh7a/qmz,|u*{&缽Z݄q3Fg^F8pj tysK@?jmzЕ1nWJ 3B꫌حttcCBS?M!}۪'ycY dWkKofk9H'2nu f]҉m'YCg c!F|Tɦe g l?83?~ilb94b`@OKAas) 5-cuzPQ,7&<]ju0~ItCwXb!۾b)9o8Bsx\dIx},:?l^?5EdAcoxϒp-*J b]:9 C ]0B}xEAH0'rjir.BTX08 )`e#jRHC:!EavTaq`0LdVX.}xtqhkj7XuT駔<St@͜i`/<8dLĀq=σ !@%\t\D#v(OriQ-1\BDQEe͹Ń$?5_&WnwEL&r105ҧjEaSΚWֻWw&.MiZdz'z_Mz=nlgxYV"@RF]$t[W]闚l ܰt%}_<=r [Ve{r}!cFVnӁOQi`52}mծdX~sqPTw_{ IW@iR-2Xl<;CGoy0 dn*>IadwJFE)9>WzH|4]>kVR||ự{=ș\=m5U^UdF@L`Zމgg^Э/Æ  EV`!AJ-gtGKYY# +,U^`ХSCG#N-T\:w(`SzyejRq%mz;;HePɪsJozccU:m𖘆ߢ_jjRٴ,W(3vg{h*!?KNTE$Y4J:6./951* R)?wcWy&sxS, 6JyE<.Y M]*cze7 H‹s DǓt`ŦK~0¼JgFyED.:Pr{IWYv $:u^HMkK8VH8c͟ "mbsvXi\< bu4D ?vt^h ⭸SztjB|QѐwDEv*DBʟg{gOƤ|JᙦLхmm].򨿎Ԍ=Q ^bop%!:F;w Uۊ/J16X&@>Bh}Y)Y%\-d=dzD,:Z BT&_ԋ&J-(MY_-" G| *j`&7e!EiZ2K$ 1r2wG@XISVho> o_›pJz6[Nć]rj4~USY|:B 98[j=qCɉ1๡6p-}J b =#A9AHiH /O lu0xb?vj#~5&Jzk5,DOԤJ\T7YҴ(v)㤪 GX+,!֬6NhRY +FlRk$;"0QkH;DjA5[ E4t=ddYYLONYј,K3XVjR~Ket=Wm#s12uZ)7A1Fb0XoBfⱨi&q<.)z}7Zw-&5ŵUF6y\e,[TIZFj6قR*ldr{VNHUWGxQ|EN&cGᬆ% $w|T"6 SRRR|r\*ZܰoZn1y N ,f}}UmآpZd`ېKgի5q@oѺUFE<-ʭ7+^/“]L5H,럾ή1t{ WC͢{4_IG|/ =",xI79e抶!O #u~īz?x:N+誯= OR1!=QA#wvώS۰<(u$X9p/w@oy}apcڷvqLF1:o 7vGd Cbۄ9Td@J1t~R,@5Ʉi'Rƫ!ݵG#JTļCԹۃ2boj1C͎r+G;hF;1x} 2 /wTt=,.VDwi kaNJܰ#׻Q3OBU oh2ˑ]AVa:4O;m2PcuQ]gKǦ&4`y*d"eߓX㮻f4H o]Zh_tfBHD0¸`;l7- v9߻GLv'\C;ǂ-y% ť)}vZvs:^V+1%Yqk[c!`çK%>#m͆XQ=zI؋q} rh {dIO \29BpwQ ^ 5\N0ūړh' b3dȘ JAn˿Ocm ,=~+#?} meaF}LR$79 Q+ڴ+-@NmSp8R |`TP.zۺkۣ.m^pHe @|\-Dn -X SNu9])sDkxɎP?T%;N9W> 3]r"dr)A>Y)ý2%k_C:?iqĘq!0F@K˔0azm r*,%RJS]@w0o%!퇋j=@}+S"O>`+9Azd$jG+֕qN]7B~nvK`9`8]ڂxnvcMYO"B-1^;疺<N3AIs.f!F{`sQ]qH;֫.RBl- /P?e#la$v18cf^7ye*E%-%~ )Dd1]=/jػ>)@8 ȓ(K ͬ=-_e\@7-;׾V=,$%KzP*9FpJ}yz~(sYMYS4B.I.P LJ!!A].G 2>s֥3aCqv7i\>h+{DGS<HcԻFbQhrIn3sD)фIg!f }DM^'zx&ohXlˤY! k0MK$]N jbWJt ~(}Mms_wd\qH97x}=/bs,U|>4?wO Ug} ҞC eX[u3bnj]DO<9I0WuPTXy^Q)#aa՚KI]9%B@r7 7 jKYH&lZs˱p@ ͛6 B` ddmzG/l5]9rӤ976 9/J;3ҋ@ Rύ(^/W  6c^E! Z hzvՌ)IW*@|v) vDL/5:v |Ⱦ(tJx;Ǿ w p+Xܰyz_ௗwbEI%~&`@?T]d|=^&0ٌoݭdeyL={yAKQ.|uܓ_=)/DegP'&?Aڸzy bTXjDꊮc4rYpŲkudm `UDW"*TyC`-?M_6FE!:DTFݵE\GMS KI)}{JN'-k/iuL[GFpYKJdDvVM t%#[ܚMd6Cu'L2Ȍzfkռ侦9db"2\@KĔ1 xxHkAF/MQUE Zj%Iǿ)4w@_^6~_N|Ѥ(̟J8ǰ< [I`2`b4Ěg10߫5VF.%0a5!v*^f8[p# @>9*r2J)QhFl"9/:-\VXnbAgU.WPGȂN֫se4^|n70p$ PT[䤝<'s'^ c..R& 3Dc_#߈ ߸Pdy9]9g%Li4SE6>,I|+w΄mIH0)l?Ii1(3Two31E$G%2A;Z屆 &PrxeIEmW&,8bO=!2ԴGܝC zT ,|]\coJ$i R>% $Eb@W"B*d+RS0H|wU&]qEJk=3Q}@+ۼ&giq^o;WZd߬[nYN>\6׈{-`ckWԼ]`"G] 6`$[ϋNr&d kldoАbo.cf+EanYp5J榨HhZ:NnrȷxgD$pbjZt4k _6~ %v1V٦%r^Njp]=6ɼOZľ2ziܒep`kV'W*/YRZW֠qRYW_l0# Sp!xa>书!(ۋ$HqHxw&sپi»XMi?=qJK'E#-?4z gH\42BE[GN>~㚢k3J#$@F+Qr˲! ޯJE|fvg@ZztUawBR=@ݖ nu+Ņ뮝Z٪b*lV'eDYW~1`m9:">6[1V Z"UƘɂya`h3{@Фo/\E0;T Daj"]dXsVCD2}2he!r6/ B? }\6yha*AVWwM{Zr6Tu #IiD~.\GNL ijY?ӛm m7KkDKbO8i"6iw[KKeypX[w& Cg?B꩐% l넀Sڦ^ v6z ֒q'|dN^s_ɀH"Kk:,I;gF\|: cQ6),o@bR8CuY0!A oovϊ~nޏ8=*XcMgGE]OveytP0O'tC2Eh\s cN 'p]@ĖTuAkŅbFQIXE}ƍ"|N l8V)4ލ|rWYyO7h.9 ( <fBh9 i4RAu!B޷ύWLWySs2 iFu:f+wѴdH`;P,>w&.Yqhq%lDv2\s<Ifr4UfJ<(Z^ lL|d]iZϡ]nY:]M;A<`< WLok553 ><펰ފT+mi~ho+To40g.۹]Y0)jpA儽D rD*cU̱oJ:r΢Y_g#H77 5 *&S"oD Ʊ1iSE3!=6I¯rahW5=M·IH6=O`f*X`5D +FE%~Ȋ0[ПD/~ĸ۲ez@ipp?J,@B* U> J`pFeg,ӊ ( $r8sjYzׄh‰U[`IZzrD(V S-gsit|i /E2tIr6q7>rjw90|W H~r)I T/jˬ62"mwxT~Ucq'.OJ nRğyȘ"ڡ ٞh{yi>K[x:rސ*ll^g)wge!bSvccĠ& 3_J6|0Fv,:1׭=X5d{ 8U_PۅS hɃD>[e:y 㮥qM2k!Ԛ^Ge rT_ȄF)l%hZ;UI'G={|u@boE1#e~HPAR9mqË% oEג +KH7޳n>c%)֕:a.JNSU/G~ Yt#RdMB6z_qt |so_JLNdMNl;f|x!`qkvqA=AfPֹdy GNH޵ ɛsY ,$rx/8MJZ|eUkdu'lo";S$jr !*=0@BQ>Z3 Lq㭌2\/3Bf hPSIMFP}ƌޚhybeg_NgVԓ{|vԯ=2ez"ժb^;^1lh@C|=K@<2A9X 31sFAN2$93ә%7kD+BMVt?&>6SƩ~T[M5> ~cZA.èP-tЭ싛HFFce"eI1R5}/ΜmBK༄j-rW;Gi RtiT-4K546/s)uҀ&9CPs7ǡ Ga^9&kuGAwM{9GJζVp⎢qOuM*((@M|he@rrGNB$k2訊м]J;J7mNSiy+ǘ-U[ WPyALFyEUtݘ'tsxwk3oB=N lʵO]Jxnn}Q3f1 q*qhb:^Bu7!T@2e~?~6La3'?/tw7]0}"V3d~Y$ïh+ܗܥ1&u&Q ]n&K4D`/H5aŢcxF]]D^t~b#aMƀbO\/|b½eKI/䟁wg7;p¹nH /A>Pr0G l'g(;[.kJ `Ŋi,6{bavqlI\}eKTXp5^莐y.dUްrBxӊU(VCtL+/"_ڝ nϛڱ֊G3D~HZR7,DUID4ΈE5ŕ k?x?~r Y7//gL+n,Y"?61OF1f}au^{C_t\[NZU_FrqV1vnB" Ϩn6Q=cK~bmйhד/O,ɏ<%Zd<#Xd sbQs4ͣk!lӷԨ!co'vO,Y>`F;x\O=݈9>k5]qYuLm!J˪; x⛛ !rقVT@Żixy{+]6Nk#$vH7I3Du_f=H>W(* ?v4mn5#9̒g*K_޵3v  JC3V4䉲5(8u:1$[e0sYUniኡ J u鋑8aPVss8S|C*֬o\-hݟdrm }aEsMlWsJu V%)=7bB(=aSkn@(W {SZgG!Ж=^l 2Μ KN FZ,UZ/da.9 ̟_+ cx:*A搝QU4Cs13-tOfU\ 4çOhW ]U]HP:!eZ$p$zn rmS1Zh@\cG gFW_e%"<ˎ~@5ؤN^)*K![h40R /݃2Vӥ+.8!^S黛2ΘE M򡠘?!do @_wFx0bՖ?Ĥ7xouyhR ǸIkrE(.I_ ^M jR5 ߨp?r Z-%517h hK.*EMi?LėE(z$Çm*~AwsTJy\͓ [H:qMd7K^@Bղ06zA2%cJpz+BJGPϊɚ 8J-EIEwRG\aM4e_^!簼Cu-&Cf{5Z3pEqTd2T+`*F 7q#10p5@#jpX1Hy[WƎכw'GPRש@\Ⱦl%=w OCǁ}+R2N'9&iG|KhEX.M<*.oS{pG/6-YH`DO, vdU,5p 2M4~9uq3u_!<3|Mȉ#(9JtU8}uDH֛b"7nt^l6vJ^4d(‹na<8r !C^c|žE\eY>mhGa⃵cH}(_ݎƷ^(LjO _œPxE.,a9c͊yzPj"T@MTHppXxCW(dnҤ,+ 8iY!ZCeUb$ D%;r ~1jMdM_LTP >z9. SyE)+z]0L ䷬}z IŊ7~(5FXboNpSeG9rIDRpЍp-*!eY׊6˵WkZod:e6tB m4[d:Q>{fC6Ij)vԲzE D3'5vߡLUG=bZ^lA|tlcyľXB Öη0-"?gЎ v߇a! PƔ50ӻ'K4Zf[-!(mD@o. mM&anС"*edVg'y%'" 330Z5P֗U<6I N&[@KN Gu)PugkԍW&yGNEڈ*1aĉ7Jm+[Z7X2:\B쌫G-,t'{FjK|'v+N-C=N9n"EI8T$AF%1{Y ͵LN%R|>5InD $4~rV*R9+ҜUa`quèsҍX\10lűH=ɥ=G+LF Pt'wj]렋W]*~%30>q8\Y#ob ʛke ǥơ{@הX*Uo{^5Ys'ndѨn$?0h9p6+Kپ%l ȇ\|Ng azFq(Z<$LUQ $%}z퇈8JRyj H|O :`+gC#ڒL IpMd/E:dUaA TثODY.9Bv/<*JGh7 4hqsv[2^%i}@vXi-Z` )ꃻ; c=HUn:${([SSJ]˙&085iPb/;1|kV+ W`ֱh6] s|jm)E婱)yD L;@5NWK|Rة6Poɝ-U|( |F+8%aKQ6Į&C "ltWM7SgbeC }WU-V4Z EplKz U(QZf*>Ėe,CPg[u|YvaJWz=U-wHlJ ژN }ddYZ|TA`DR3ݣ[i©h!a.B'Dn֥"B("szɃԵ? mK'+_F^[ԈA+X D2׉nDr*B>Rl!7tMcJ<Ƈb mߙ+6#A V KÏ Zk{\W_vĵ:f^wc ѻR`^N4Cx#8ٟs=Β@3PJ(Qw5kj8}*@/ĀoPjFn?XU- s`7Ž-&o_+35t$2DM9Dck4)Af<*ek92"6;iDsѢrOŢYlqZKq(H@(97_%IF2c8Tq?%ޢեF/% br8 v&BSƪb_źd[pT`E6k-WS=yt:|ªĘBӒD%c';$Y`g:)c9r;F)#W@^C2%W*1u8,D,ב?e@|LFO=Ȓ֚ ghNꚌdn|f7zP@q7)\4] I. (}؋!Dry &vQLߡ@VSɴ6?ثMKϚwuo=agBk#ÅF2{W9w,h}؀DXn}zjn0Ԙy)_3vÓ- F/4X 7"Sz)΢.dJMMXoS`'뎒YʺV.E9Bq,16{jm*~u뛣*PK%(f 50;p5:fHg#E^ҘB^^nh`~ RLѻ ȅ~5N+E"ljޮ _uo¢,ގpڀNȎ5+sd!2^A 1[hgИARwHִ9ЋYH=mI%Y-mM~\Q yWvgY[ s )(<$G W-աׯ8tW4COR&nУ, 72 u}CC۹g 4\SdM0C>9 >Pzaf{#|bvށp5n0hґ-Tz F/FZEt&W7J'OTXdx*F ܄׏Q7'u~D}0[F7{UX嫅ˋ6A ouQ2U?[6g:9ph-Ŏ'!|PW(%5ߦmkQ]Cب.GVg\X/B`\*!oBO @%bknlz:tGh5 H'̗{丂B5}h]ilTqQtrZ=q8M9x.T#W$^a+#ljBgR#zqF;:-T: H,%B) bNI!ֳV71dxkTO}VWL=e+056W,NO:8WRA _}lW -قw55'M@ o1[̽P53+?ӭ"գyAhJ>> Ú$߹ S0#,Qb֋g) 7n\h呉KŀJ"9 HS<&!.(TG R-|Zco}$])QuQI[Cgdp`W#`J ;OB"/%}ifb+懩$BYun.o'Vc6ԅ\G T I(󜉓.ڋdAN$wF=xɩ P EM-> 6_ᵋ&R%42M&ΩśA6=))sЃHMҎD=I$l(ug ;|HŨ紮Ģm=x-YxLݳz*4p] X-h$'8Ɖvd;b˛W 66 Z#K-7=P.Ԡ8 'ÔkD2HsI ao0훨/ !koS]I(<<[L|>L+k/SІPϢXA<x'~啤V1*m+æVr}LuPЄSH>Nw˕mb@vT;'[O˸~^\ YĝLE<^W"-7=簞(Ari/ 3Vbq|]4aA %_lw5:g#1#BR&<x]?dra? Tv6"`|shby&͆?D9z6FG)ʲ{v.Sz@ݲ| `/3;8 ]/ܶ 0 &wOCBh@Pj~wFK­G&G[S=5 Vu9BD>=I t!~ԶՔ %i򣺴x?1VZ 0O'_9Z6~il_N>,H.;'GdDJ "(L|{Nmf:dUÑ}4JyZtK٪t-:o&6s[㬢""iv/M).xͰ엇sJA|r@V4ұ>DIߤ4hi{YRmR=_\-ax M%Qr!1uq9iAQ]P/|";V x S K Fӑ ^~KP/z;縀d|NwH&CWNP2o${^6v#ۆհPܫXn*"dXwF}8RMTpox lG!𯪍CO"$ cdAFl+TVdgPcymAg!Ǿ~|;7&<I¸GCrotr9ss8NVyp&lwo9چ*M _SьVNNj Uwf]"(m|^.d#e7);e4ݩ(EW$}YLZHzlVew?c}ZuNq(aw[4֘{d4IE1;uۿ*ȆB 6¹޻А#ޤ[U~Xs -CViX^ ?-JM?驲-*jտh`]0hb`)~~7|ٷ-XsG B3X# -ctLG32.wCG r'$ AuFv! J7Ǽ #LԀy?tgk}NzRoZV;PO1xu`Ut\3_ł/]O!w9+e ZF"3jBMU'|#~SPr)Lz."dۢ.S>֭}nJt(=^_$tZQ%VQ9Z$\=iҵ#؂C0<`k|ێ절jʎdӱkJsY:2D>29[=oYǬQnz .Ƴ . =C`vVP4ՌPJQ`8I~(^2jNzkr%p!;K\ !BȆ-wMWM^ۋ @(v>9gofo=>3H4Q'k;/NLDἝū m9)~nd(:޴uw\(nVgh$d8bRQ|5EsoHcT}"昲@E(]?έZNy'~'y0QFb d*{BpHkQ j@Nh5F39^Ov*(זgB)Ӳb+~]mQ rRcߙo!IpXЊeltۢ~.D-YJ^"׬i@,0:WC/5(;_e:8n“auN[d -J]}f4d.2D9y\u^=uT`OFVP3fUv:TEUD{i['Z\i*Ih4Ĭ"oky۰@!"q[q;D"cțP9"'&OZ>}u[K_(HQ?Yʛ}GhXO\ _':Fg by-B $붌{>q0*ikz=3#X6ZIyh| rd(#+7.+ۅ*uJxdq&lNtr6v0X`6DW^[YDp@]J4~ŐYĉn7Ɖc5/wiX|@!Jpªsݯ_1c=8ږ6R gehx]DO5CJ\c@#[4Gv[Û!<,&~kBvؐ1`w0'ta-\1J[)T Ɠ0oY%8cۓ4d`|POqxC8 %Uwszl: IxfpMF;|fUoiMR;1u{a RғLT {ϝ{u|VưAH/; )3hiNBTY[jJ6 2++wIPx9* ۲r=fNo'oZ%^#wDrHv) My Ld؟5yUޕ Ctf342DX v|$Cɀ(d5r\~K t"و#C 2U.5e+JHO,ӑr?0dLLZPI?ӱWsp4W#WJh&&'_c@бb›{la Z[^7fjIM_`Vn,P1Ӣƀ$$ޜ5kZjFZ.&VcI&2ANNHY/M+uK:ԝ_Qs֓ xK/W͡n4fThHhDI ~;ɖjvμ-m#câ_ie)It4 6mhV ozR0+}La; Zcփꏨs\bh,?1p<$)YPH X4!$E)PaȀkmqk{٭)O!TFG ԟw9'd Vý`ʒTc@pp:L/שݷš`y/t/! pdڄxgɺm[d>#q^5=6R?ӷqd|c[(7~K-Kz[| 6T 1A!5QZbTg͑Y}ۦ^ɧy3SS9eo1TaBW-}au"f/sE>X;{zy|M$]6ް ]؋"%\I~P'}>9dnó Ns^<& ܊O햃S/"Tiaj$j) WVjh%F`b'%h8&ݓpy< % J$#wEy:3k1k.ՂY`539_ = 0%PɅ?D;&@֮9\oLM|* N?2̯kK94I 1E}M$"2ݫ˶Ghx:C?7P;}2fAGο4BOvGS 6lw"մ|ܿzן/;jy ,{M`6c^8Z<'!kMEFK$4,d LfϭOv}3V&L.I$<*O,_h`hǑSqz׹WsY"P)-Aa?-鷝g);֐傤\5Mz}>M-?PI<՛w1]iO|e2[!5qn,>'`+'ȊrJ'B3>b?-JT|xvi3@taM}&!0|TAmG?*W{t>ڼӼ TRܓUPڋz6FEreKTO!C%W<[_pס}0OyFXMl[uc7 g 63mi?hBM 4Y?hՂs;`dAF:D U26óaH+5uM d|(o@.<T C8|r*JeP1ay=$'bΓ^A2I8yx@٩Qv|vR7Y:'^+ښu4~B:mvE};ļTx?2$gf$6aD[$;T50 ~A^7<#  _?z19"n A~ zSU0%+ISY+xoF;-Kd=X]@J#[z[&0e$c/w`@M1Ύ1A6!#k8ʓqa!|Q(xL,nxa-扒,?OK8P rBAlFٌ)U39My^z!bUSXF=DoBA/s1:JApGS=bnoX͑ m@\!4w b3ʚ#imP'AA|1B|).q-n WQ\mXN}3Mwqe )dxs, MbCɳ}J,_1S@ XP׃O/K3ؤă (8x;ҜnM+@DR규}+* ,ar ߦarq5Z}ZPdBp#T2~lTӊbH1yW{՚]#GdW" 9q/aJVKė/F.7~H]&4)^<0Ig%uvW3R5RR*vϰpc~+J3_Q]ܩ ,9 g0kbxT'SE_v_Q” %lY\ ti09&⋂SP΍nvInKX{82TPrl2sGlf6+nÐfg:9\NT| %rv=G(w )QrlH y\{MY\8_Ca _4[Sf]L@U29ag |o<;eW}B?YŨ:WVU_s_[0}mX=vNr^CA=0]dEka2^r.?v# s-av;?)!0_"i+-czzۓbJ h*>eپd_.~=O`"XW!>)R*0]G qA0Ud^qB|g{~ڐ3f-sLє" N|6fuqi+! ɻPeav,=޼9h|j DB.i~*qsfz3s` $KS+Á2*${j'JW঻<#I,_R(H];c ; =n1@qwqlg==53UnRe/OtjF"# }O,4e*d6kmaOIuuwW PzvOZ|D"Dơk_m|y_B?g*'4[f=cZyi8 >JEҦAѮx %y\h,Ջym氋կWwcyL 겮ujۮ{Vi1\#' h( kCH*yJe^q"ܧ So>_Via1d=+:+אGRF{KJ}ј]5Zcǟ@A$z_WWi%ˊqZ *,4!d/}p0耦?y.QgIRkMQԑ:~tY8DɡZ:Ugip7).%5BGi޵hTSMFEi}wPM1,թWw}zJ(6ҧ.uP c`cx*/{ɝHv33m$uhy1h/ޒ׏PM hkb~ 68}ɺ!8/)t,!m\C#~޽L̋v: z|X}~&^hE,EQOfB, dTbč//9,i !*Mսmt#G>b3st~m* -=*qDx ȕN8K*=HrhP!BD'+ A}j}0&>bEn/;bPYQlJx߶\3KI}o"]MDS^) y5Kw.a%KC¾x~*PƙtzL JDLNÊM<5Fg!h=/+ Q+젶rvBJd^N ۋA1hZ;Ӊ 4MG\ͩcn^\z_B'JF{7_:*wlxپK.p~BXbV}iJWN7w"0\PWI3%~bet7eoGTy(!hfSUyzn\Z4HZ}OmpWһ.wDy|" |oGk 7RXr) .lx:ӢС+i}02&Eʄ1*}/ġ55wЙoG! HAjvSD@O \]J!ER%P 1*@49\pP\vڱyrg3- YUt]U͸,φ9<3 K -1,uRسv7X z9\;{vM>{~|\-Z_%b~VF/H)QtFٍrn3Zdgj빖̧g:?4SMWQy5mP qVk0~qb=U~E: rGC!g#K{f#Q :$2D? jGM2XpU(cy!20K@xBKXj ٜHT %Й51#+XItF.x{f υ va|r 8^P\2*JsI7oP!*+rPXepEt8ڔ(TXp~XXk -@F}e61ʴAnuB<ᝀФh^Xq8Շ9U;vmP@rLtwQ\t wcEi+&TqN @ʯ7S9<b (O1o=X<' U輿^8zwL=*D>Xysn\'5V^*dB-G )!= 3Y|uJiGe&B;YA{)3%u/;Qƌ21: zdޯˈ)R) zi@pR p ի4i.oii[2oD V~[849>-@VXN{OvC4ކv$W8iDHI?%V4xQ7VE[Fm;oIbCr·m/y~VtFa, WjyLm{b Mj?u.7i)bzR8wpv),l\< RT,ʑyo蜻(St=x.xGͻ<<4Dy0k1ByENsbΉA?Yʋ7Є1.oTEǵ˄xY7FG@"ҋKƕmӭlzKoSIM0圐R;S6h{p(BJ:)!y[g\"u4HĶ+e)y"KI/y9͊6oZ,s|7z"t:ۡz#* ζ8 %:` < jluqmvƴ P&uJ 6;#>t47 [<00~s}Vxʀf6-7*;/Ҝ&G3GxDr;MY| |oTr wo Ũ}b{J!#|فj)AZvߧ-Tm3("DPWitߜ~Y{">%#(DO<-t6Gy Nj {r0⫧'o F7ڞ!qqQeVB4 ?{,ll(v)gu ,PQtRy]F_ vk8);-6aCy 96u :|7>6Q5hp5Ԙp)J 0//_( Ec~vlLɾ J7*Z+14:uM+9nrү/Di`Q%dBLCIV 9z;iK~dP΋Y%0k{Ẵ6м˜4t ܦ:+p/$͐"ŕmi+Hߙ_>YǺ#$>3ertKi'+E$Waު;MԘAn}+g %ipn2BTԪNn%Drrm`Yjm__8<+N`f+Ts,@ВmUn2'.T`!Xg]-ޱV[&ScZ+a]' g d4D5<(ST_XT5͝f* ;@Ęb Rf\SrFgQnS]6ԓ"!Gä\s,B/% W_ܯ_$قM 9} ؠS>)0Ab@|[~Ԭb+pTcAӻŢbi{.Ba~toݻ:_v;J?&;;l.ul [p/9 k\#Y 'irH RAꍛᆥeJD8nѮEe.hQN Wbb[qhC.dsE|QL5S@ŨeSjtƊ]P"eQT#o] :kYb"IiPH7][K_ vIN2 v3~EбLw;&l?!۹r)tā *ú|CX?I.>`;1XG^kdjr.-ˋg1~҂͒D- tߤGg)M[;Q'^:za?MA t7<,ù*kME_wO pdD W/ߋK#FC Dked@˓+M=J?):/JJMqGQ2]JCPCJN@=e~EE ",^훽_)Bиij}YiԜ\/P Pp#pd|ޯZ .Y"R/uk7VE mUͥ?[{iKAb&##<܆^r]hTG|S<p槓/d|72G?Q3B>2z$rN;lIJa͙eALitMl޼֧ߔ3ҞzXm@:lt#*׶T iy1a<8Ў2{rpp;,O~Z|ѩDhȎjUwȩeݾ܋aPnZVGOȨǑjY=l: ώ&-_eYܝbͷ.ē0W ޒ6Pyj(ςINۭbhLJoPn)0l62o1:PXO8N*:s-Sy:s NCCYyqNH"ҨJ,c| ][&URɘ1Sl`0j mfat_Ccjam1J ;j_44it/;#9;!?fG@uۓٽM  2)͖fu?ONe8p'5^s]r ַM68[jBpXXLc܀xc327+p!3&d(2w1vֿA ݓ7'zLT9S8?گNQG^\i ڋ<i={yetjS؄JB[97mKbpFb>#Qv7MHd}8g!Y!-"4^UtXv7ELT&FiF텊8[l8L" 1E7*oX]A+ ,'|5=?L ^h7-6/7?~M{i٦_/ P4!I,P x *red+v&ht(;PAA$\<+)a~ϡQ.Qsdf妤q(#0tYh'PYlNA{d̎2\c; G ? V!1@r7bPCOsd* l] i3Z#ڃEnz]@B%t!H\c•'hڑBࣂpȢTMNsTYKgp|Q7{Kpo0&VTL,aN]}QCʇTyq?Uw~!1 w6P-9C;3UqM9ƛlLẓNkYbvCJs.*;K8%gq\Lko͖Z:2z^_mPo038?L~D+("D{;[AvC"|Ag8HزwB͚IX# pLd:D9+74;"P)WZ˖a7}a6Mz? b;dyƗEL5{`U19f+RͯjiaiZ82J0M}R~;9 |^jlqk3{jɞ_[!~>GgrV/uy kӨ"X;xIe?Ȩ Az^+P3~O'݈T zƉ"HȎ]WEp] 1~+ySM*w*85_B\'I;$k<8 B?w]䉷oxUg=Ê?,Hs* e\ϳ8XĈMV;WOي Hsª'~8+ Vo3[o!w%_s<["@s~ 4@=bneU !Xƻ[uV#Š!<{6sv68HE#~œy ݽ͗}xi YN0]S8̔v5qc?פ,qHq{Fy1oO*%XwRRdQL!e.Gn7#\͙Q2\ &!mY'$u1:㵣='gs3\@O)Aac8edӥqMQIf=3gʍPHw{Wz#!QGM! &I55)W6|iZ#܈-cE)O+ \]JD a&{?:&PP{9WBVr rYOl]6Pso9CDt$Ÿ.nӣ+7@X6hMCO::nm11wYۢ :IKۈ` >lpJB)K+69:T0ra$ giu~ tHEL 0*@2C)ͫ h$oD: A˸YBh!&.W,0+nUJ6jyJo"a2鴿+;P4f; pG+w)zKD`>~g3f5Ɖ~ѪE]C/}7hIxEA:a{cSwDrA[;q>F"U½KGrע%cςcr$q宖ӧ( S!EAvcjoe)@>vѫ!=MX:_F8pd,̣XZZノ b{ɿVA" u4CrCny0>Doa{qsf@f}rcFw& oﲶ5FsF .=H8wt'$*LpcF'0&lsH a]I luwUC|]l1zY5m.+Ia}0K @\|A7a[MMf6OD%$qЪ )9|v1ߙeh[[xvS0W}6GlXR{>.;{呙S)jL/QD&5G.l }F$>"9YPD\8O%[ew{_x;(Ff6nW) !c^fi>F3]l=e;ش'SCOI΋SWiΚE@=s"~RD{(EV` gq2M$MӂA2Zb[\I@^\チvS*}ZrΉ &0u)W$QH!!#nԼ .6f%MP?KbHAzC WeP]V0ѿkge ͍oX>LyRLkMB]. H阅L.,_ hc8h|\?ngdB3xv&`~ fv#YL򶼖 JJ# R䏚wH HLik\8D2P{%xYyRuG Ww5Y e{9(I*ŲI4JS^Jc`nis"9[IЋo@RFeSRqi)#RTAD(نÁRjtZ΄;gȏ6Z:`xgN;z<e]ĕɊ<\ s.4Bԝ~[ڔ[݋/?,KսbrZ+V]A-|7s# d9ԟ)taOؘekmcA *AHvbSB$ut?ڣ\F#9A8 o[ N f–>Qd^/˕嶜yZv[q\ur@y8H)\%x7 M,JTTDbHY˞j8vۣFԹC~y0=͓]1ݑJFK}JgbD pI]fc{/Ȃ {.L$FmeEnAcTĔ 4ŒlZ-CԨ mI:J3%I1Ludj*di}Zd wAtՀj^;[ydu>Bn+} v39mY˙uHش@tY*Ep' ZC⟝RIFKp,Ȓ5WbrfJnPZK!&B98$Y_ek(mojѱ NN5vHf;!Тlvav2zbt>.V_,{1]fN/`h@/_sa ?׶ٸ4M-ِ%(FYB@]:WO&ct Л!Z+׮+2'Ȫe$榫ISu/8էJ-!e?c~m7@YbŅ6_dvʪ&H>!ΓR`pfG"1]DRr3ЎzaQt1; A8K-=ɒHr -ʳ*WO|^1VH`hã[|cY5@1PjC"<^k]TJԙS"|mh&v#XWQܒkd$Zb,ݠXS!^ؚP6ܚhU(0!xbȧ a=4S 1w P3c}Cs4ux> Nqp G)sFܶ C}z"%"bQ zv].uR7G<Qi;f BW#'q[0!Kl+GBRj5nU6Jʝ@|4ns[DTzs\JKhcސy)Tzog^>jrD wĝN[B.ԭ?aUN#IsQz`޹)^}W|=чy""Ai2hALUSzb~$="&!%X`Jhv6'jǭ[+dfgUrpH&؏m2, i̬``UIJRqbo6| Tm뱯~ARbfhGSǡE8\O)oh]CLZVDy/̱dK@TdU&Ƽ|0ɶE; Y꥾;+m,f @9Zp_)YS=⣗6Zu0Ot]:FzGYӠ:.uFɳԀhP8-U0R3o]DFt1aܘٚV+џ.4I>O. Dl).uH6uj%\[Tf8S͕S;"Eo}ΉSi1x@-2ZewPUim@{fVpꂮt:Qb Pk~_S9'kT%3A촬s5Y:@-8\`i`2F]PT$҉AEB&W:4QVŶcL%JGq4g&2˅Δ@QzW[hhR#J{!髺4 j'l~i7" ;kqnumqt-<+*]%\Ro"Spm0{K5du{^_f9"'[P1:ɞБɅ0^hߢYTJ%O-T1I BWvUeܫM?9 +[`>9xlil=hi_ 2 h\6(vZY>Ru>q.e^Dc=;3 )lDߒ^ A/)?m"9ӥ>Jldt6}0Mw1^8_/+$)Έue(3d UY}7g?qӐaF_ο*5D !*1rg-ԗ%œ%Z,&.!Ctxr!,I0 !Fx3() ⮄klo3厄$=.6B mL9s{f)W,؂2 k? QFߔT .@HBwY7YOQv0l~x7aU&rX\oH2Z錥+xH}cCHVF5.Jf5wWݪ#^Z"~:5nz ZVt2\mZ7/Y3E F*Ú" ~Fww(*qfCH1Wv[<_c-T}WqZLE38 tФ?,TwZ7JAc[b*Y5p.+7JJ6fIPte+z_IpgdEU4fω $ 랴6d"c0uj>B* ƙj,[PqUpլ%52:v \-$3. 2CɃ-KQG:W`\֗uB}-jiK)6F@R*Lb:( 1$Fm\nԱc'KW C>JEKA4WrNjoڸ~;)Ÿ鼂1P.[wz=r3?ٿnJgYWnƞk\:M*wkIa&r*1T"ڕX򣝡G 1_]!@I-j)ehZ^q(dHq?%Sk^ϑR;4.zxq"_b(X@A-ucb.Ὅ=brNjz(ܚOdfF&$=eڐy>3cn^@B|UxXGoћh_`mTK/R?Yy&z&QM4[*&!u/jMW{Mam$klxDѸ?C iexYr܎FWS2ȓWo$ KwlgVB*J$<=]io:adMɢ*Rc\BF ]so3c11S2;ߕaP%5S$~l[7o-Z.)rE,m8X3P SH (zI;¡ 4 "HArH'<1MW {m}}w{)4]-qk;#u4[JFA. T8gxϣ#J>q>r-r }:jsekCV[cТԹL% ҴI^ eoÿ'U{! @i fނ⺓] Yb{=uq>#qކc4. !GE 1t%yPJa>S22@@ӄCHCZ=Us4A!ɝ2rBДi/96Ų̦y Aҁl˚qJs(IddTS^mT%V{,(5;΅`_.7+M &x^91^g򖜷qnE{ Կ^K;6<3tvl2g|տ7p&ˆ5 ?r$@3-qQu6 WF|ʌ Vw'0yCFɄM.LNs҆b(R+{!",nhE/nͽ3ZfJ{#^k&t9NQn;`gѮefsKg?T+ɒ\6a_r X;=׃]u ~8n@]P4Q5>?`vt7%E}V]^Gb]=Q_y ];U= +).xcP+7/2Eu n.^ޗv;? L P~ˢ5s)+ .e;~ti*۝1(=\Z=ɤ#PxŬRs|GK Hz|Z=Ij'Q?&0X 3hLv# Bn?틽]Urg6x8('76be7%d6/ M>ܳ=PFf}ZH6 Jr`t$ 9ntF8 ˕JYqiQgf x?Cُ_" 3b΋AzIqi5VFjkN#ӥS6Ɲj3A+W%m2GlkߞZb9*<lk~c[/ɼ34Je;ϵ\'N KŒȵ[1mCяR;qؔ3ZK禉[GnjhnГ!@ 2/Y"jSǾW4ȡa-#2=h܂.dF@C#O:0olQ,n{wh`2P߶ ݵXM.vy<#Gh*[ t4T1>p\2}_K-E wr_{,wZJD(/Io#k(:_cg).}K,g1`Ny>ZPZ5tg#K1ʏ+QnZ9 4u% :1?(&aR}X8Lq:e8w N\@;א̡B٧TӰ"K#?{"u3|!,gw^6\BB"j-050ZewZns#sxsЂ6sV@u +z+1i ƭQ{t nD;WH Sk Ȏ(KFg68[X-Xk6:ahqљph@;f58T {,=o0&"ЌqvexD*9 x~VSgemx? dΰg՜%ubH7*S\Oj<v=PdNH<.4_/ ƭg›Ob7>0ce T'\tNK)VnsF4-6*P:61k~}~fGC3l@?5ZnwQ8Ye2?\P Yȏڞ饨~aEuAC3+E.֊0.r~-#[E+dmQg:l=R%"5~sR;snh K;A|`| "̽_GKKM[d$M߷?Co)J,G _cyg J٥ VRuBA=g.?]@CX|X#x^`}3qSMu'X&YĚ;y{` XSU ĉϝJE!լm%$"ZmsJj#Eshk@J d{ SyHӠҎnklA1:" jlʻ#FZKWA[d$fX\SaI WwȄ3SB ,:J Cחb l[n?!a_Mi2eiB)O0YfݿC>[4ܬ)1'A؛v)n-Fx^b57 ]tlZ:[*m.C=c䬜Ӈ~$Y)uN;IJA #t#*Oht ooOͨai둒q0\_kS/oݱ_Z.dt,xA)b792chFQ5 T)êdǵZBIWZ\RPrN"|9o m yrV?y12R)'׶g>@KIm\N%?W&vZE"X\Xt,YRVv8Y8wM$FU'PqUuՊB̹|U<'T1 * \iRڃ-6Eǝw"HK{2M'F>?a1z*u{E;9j֟ {Յ̾(CXMǖ r+UDY 3 ݡA؉x a C]d w';z|Dc3P_Ϸs8d)$@l?mTe/8J|UN"F+.8W@Dw7~HPv'e*,@kEH~3儒L_Y" 82:^MӔb -daa W7q=2 c7eZ1ǁ# טo2q?|sskdP*f{}p4?m9C:0xDxs-;lyNS AK]{"{@ǶXֱU9 SWGط$6I%4`옓o2w.6˱NP-%!Rg|rDDI$rJlDkr@c6Oi?ZkO>yGUV!8֡sqѿ5RFYgYhVǾtt((f0]Q8 SoL#^+>u:)?Z̋\OEW~W9#~OsC1*)clRL%GLWpedI6.K;`ys`;{@z5lGTPwgk_pK"A{~@7`GV!L`Ņ&ܨM_ϐLFKW,Y*&a>ёm &nY*le/]c3b=T2~|V, > q| H_[vƩac5!1Og ֪a9 ӏ4'*<28:V\Q(j-RfٮLcdA+Z]Cyʹ8QM5(_BLT Px2x췹eh<zF6y*B! ʯ ^xtɱ<̀ D[ 4U͍(@/r_}/X|'X#'Jvv~qa ',/Qxv(S $?/5O m=$];E2T 'rh d4 s,e^) N@L9[5*d?up/}GsuσHU#} woSԸ88=p8^se1J ?h9quNFZrC\D&Eijm.(S&%;>DcI@h[Ɛ"k59і{(ip(%R6Zg8q劌rA:1<9G0vH=HU1T¸5V#4[%֋|w7weg޹:vwP_ QR/r2{= )kd&{υkm6  ҷk~=np%=lJ!v{K ` ~o~WƇpTdr1XW~W:qMy7ìیN7 N5xs}tSY[}=r!~"fuRyQ!"M1c۳Uz1/9[b#eҌBAbIBiĒfs2n 1WU x8X s^<71؋5O7gQA HM. 3%SlۈA(x$N Nw!3wqi_/hhQh2ДJqde9:WJK/yWDDLCѣ8l1;R _JC% 2@^* wXԞXːD6;#Yg7Cswe|E-IP3SH}L#V=F; .Fd61z[?gbͫd$]Н>YOfJrٝv%Ӕ wVuaLy8eXՃϷP;9xA]_WAk}"mɃ>B8)^\m[VsiTm$ Yg~Ъ<k#-u \x:ox~$N?n*Lj.r4ǭI:#1f;pejKNe~G"?ks/@hav?f9U8vg-i $*gGS$WM;" ԆظAlUs [?Hb oKjw(N<w!?qH$8GYjrDoVu |Z"Ov>T WyܐÊmߢlNPĶ4,/ \X㸚kB_z\xc cf OA?2`Pc)]Sp4QP"c _xWYOv+8khZYa/\uHtƫTݒS_(GC:R+'g@|_01`=hITk<Ńp]1?E٪s',zd*̶C ޾Q:PQ.O;l P#!a>jOL~ӡgQ=sfI)<EGUWnF1H@2\^9 E6;^|j.G; C#xw}9l*-.y`ݙt '`b&wM[_$Ŗ%1 CҚnxNRv}ZD_;P1r(vj2Mʬo`$#6)}\+VoxQ I<G;W7UJȞW}𼭯,R=::5\Ŧma6Vu2*NXu/U TD;2Ayc{0,̘xga|5b+ihd+m XZ.,~S{, 4ؒzyϢ~6.K (>~aXo^?Vq0HčV#ɟ s-Q DNj|߮)悫4hy0Ŕ}}Vө6RD]|\ӒZ?B(AnL6 4`g}Pl>&㉹X3oԩibG CXs_ m9;щ_ ʃ .=DLR8%_zնVw/eէSﳄ2st =7Fm'7~Gƞ.mOOƂ!o#k]C:y)3\aڙ$VڱZm~Lie M`Q/esFea˘cjb1h5^ 'e,xtĚD|`JEjP[œ l e%z%y X -"{=7z?n.|rO>OHcaU0dfsQNFkUYݜ$T(Ub=,*V@&Pg-kV6i%CF0$6CU=E@+ BuY!biݖvwC LX wtFɌ $ N_'w$rI[-t k',ߛ+-+EadžV.4Z*ԇ'x Ǫ<5X/o|Mfsy:=]4lG܁n?1Bb"G($q7r_8rq!dnvͼ?M?VH2$OpRlBNZi#L{lDDao՚e-wܧtL!e /W-*NBD]i3}6!hjsj7*Ƹ,é^g1m 5nN)|LoU6* Y#!ga)±[?M(o*Td_̑6Ϟ[ 7 /sN^?qEiiĚε==ΐmo'wx4!~'-|q; %Ձ 1]s/dsã8,<( 6vѬɠ"~ tY\`vPP+-Gl7xn'Oz*L"@Lk+ֲAbM'z+}CBhdgK53E6%RD#1uA0Ee:* UDtUKރ("3.5,tOa9੯\iw,A+(;# D4^G B< <\BZPJrQ8`NQ@]##lY䷎T#r>'u;a CXi#^wb;7B#dabEQ50Xsk3)H9+ETrDxv\Oӵ'ITNR$ RZPtHuYeyt3Q)B;,vB5d<ѷѷ\q홷SV i'vNn,.ݵubJuì,(C(. Ò̠JXPmvd_5< @]ϓ0%/yxoqDn&J+ C{Bvݕ7IdV zњh-u xe2S`1g}T=QYJĄ!KSf}yN$mn00v\ 쓣]ϽtIYh,CUmL7A;ެbe de./yt y' y$3s}8Ϡ`Gd}rq"&4"\'Ѝ%>~)Q "ps9I o)nX5ie/ ;x#[(_<~V7ԬpbGz k"fÜ|$2+2RS]Y` Ak+!7rx H ~KC[儃Q`b!\)-@2NCK3H3ȟK #,{1];pi@Cw^K;P/]|dU'RN+Us}#.m#VHF[M}GXS*'6lL]d Pl,c7I0.Tk"-#B?EG 3v+,W+~uqT9WiHn>"oNy!)r|?vgx_{Bl{ݢZn1Wn[G`T;X-*n%ȨyhՋ 4zS5 G3tM[L i3!(탺E |CBMϝ5AW޽}?~45+V9$Y׻n$L=U kαZ-u,IVdPUVܕϛ'koeLf}a-3дT*'G)}4mL*Ap4ol:0TO}6[dB_c Ұ$^I.J# 2|Vs^[У?%d&b ĻjRd[ݫ䟋3X.L$:n/LL,湅'I~Gɉ#]Glt6Gc*y/@T.ݙ $aIY+҃͟x\kй<[܁u!/zԅpTl>2(nV740]%ۉȵ9w5Ekɛ{~ju&ќ*52dC@}x]$qفڕg[G`|Hh\yRp> rGЫ=J=wE O= Yxr13TU:c7E3#}`o7ŌܯgJ:Xvd+vZFLbUE_)F voYINdE50FfԶ*Z[`N 'Z'A 1"=%#m%\O\^:!ݳ9 Po:k$B5nY=jo y\ /z>d<.QUyJF[1 7 ]Pkp #zol;JL W"1; Uty/h}Zm _ Z=M.-,a" S_#eJ(?&%7>|FnDI$%*%;g[0Sœ0K@˚3jpWߋǹD&m$5t1ή0aM=݉# A˸ME,/Ǹ ߊ C}6l# *-ïdWR PFLTNzۏwc)s@ l5cMcIK~?^&҈<_@s3> ) ܭ0RWbgnC%hDΊvd-wˢ~)_2x"P3Z` -#!|QMkϞ7ֱQj?qaf C }pl۩}wnOp$0<\~·᳼`0(=K#W^k}+ºZ?Ssn-JԄmC#SJ2R bwKj\43ɏS*s'@ v&NF]ùҽr7HiLQ;m^lFFJ ASX,K#aQ֡FZ= Krl%څ،9T` y.Q(]qVCpOb k1`N>_B;$.C8H ‰K~&e*ꔠ`'l e祝kw\$Ch|R BbBBNt ꦟo9X#(NĤpCC\kg77"!x>:貇lݵ8-$/Gw S\Jq>$gon߲=LiUuvmЈiyNFݼ*_/Jgۨ&(q:]X[h`OO9EkH(9S<ϭ5h-$VdWg~gU?$ q%D oJdޯ  =ϡ%mr=@8J7#zPY82< W{7"'ӹ=Nͣb ЊB>D?`Te.&}.G{t^pXѹO=#nO\7H!hsPY/Lxa@U+3HgѾUPrg1J Jpԛ[͉xȮ٣v!}ݳ+9sO+L~ É>[9)GG`J3o'.Y ݢN 1cLl^߿ҘADך]{bL.8R_tϛl/cBP$a IÙsd\u΢|U׬3,3d`Q?x,~mH~T2ؠO>Džf&U@f(Ѧk{UƵ$7,@s) A0+W6Xb:ifJIn1(+pORLe_Wܻ5յmvһIs)] +b{y~6ɵXoJ(:Kˣ :M/j,HZaF`@ҭv/<"[hiKI8`Wy\|Z"slu!`*ŗ]XX}LצԷLTν8WTU~yl@4azNϙKL! wf{́Q7VĞ@5F]t(!dQΌB\&*\\cwLe9{@ :Ds x q|=nj֏I ̴@߫87qcª@b7n{ h=1q/bd F<(]*iS ę9.0\˛_'~'w [fQ>#ckmWe>BEݎ*d=z{!A05 ,R%Dq]0D0Mm]m`ٱ/Xӱ.J8ppDqbҥ ki"<kt݋dިZloGeŹ=ap^n/.@fR̿[sCejyŗVO':>X)#sag'W q'˗,7Ϙf(c e9|}}Z ֱXw+RM{?flPËȀ-t;^I:!ُ(3,WͽcA6|A\'nYTh~`c{z;ŬJfU?.qόJTdf虌]1%zy a9[Za~G<&!βbge;Ș,+w^\Dkm.r|b[*cpc쫇#1'\4K^Յ1nxAyMp:Vs,vn;^*!]ӧ.H/1ZAc~ ʚtێSZz`iQSbި*Xtjçy" _p"mM͛Taس2"^]O2i^*zLq|p3q."S4U|褭.u-0ߏD`VU"]psiQ$XR E=G5)6I;`?&@/+BXDwtΚdG|c}s;&D z3}"@Wv#YѶ}cL^,ɚ~nQT^|pcR"jdDDNXmCOK+]gE#eaaScKNuodR({^/zJhXC(>e,53C>3u招EZNⶔozrp;8=ZfJIAPmbqFȋ7;1(E\"?u0O#쇤k6!99 uʦ-3 {z.Q:1^ ö`u4:ao=Qgݢ+4sS2Y(m>eǏ9ˆ)|!J8S̷ V{o[84+'q+W)k-/InSKt=cN &$څJ+=@LMȋǬuxZ(&Ƌc,,dZU/hKRFb>&t%i.淅])ߢ95x%ͧ–הTsEE-moˊUKn8=(X_vlHHF&Vn֡`*䳭ug؈Ocl '3sw"oZka/ap94k(8aģdjBCr"K$Ɠs#`mYR8J=H: ?.\`c&n^6zcG$CΧ7Jf0L|*5>ϫ]N{z; BS 4 }>@`·Ķ[ _z[+94P v`4fVEl׶׉xՋ #xT[w7,w#0z[y$BA,x"34\ƛ Z~zQuO2A?Nն6%%Er5RDȀe ɹd)tXgP8$ɠe c+ޛߒXN'0/5IC+;s!ϞJ1lUOkD읏 N뮾{XEK˞@ecdOL\WϋPYJUd*h"Ҵ/\5KdY{Q"]h 7U4Ӫԥvi0UbD6<'+4&y4XYg+О*c??۔$V),ycK7΄4s,ilqp[-&kҲxuk+w@ffHOW$LHYkr%2z^捾{Ejv)G&yȞӁnbpK͋X%Js (ϼ}a?ډgJS=z醷&:`5L|Pb[kՄYm7\k>?+sJ+qh)Չ9%ޚѵ4+y3(̙tv2;1ρͰ'ϧ\0;Xߥ)vVp$DtPy4"s0[$9û,>,w@kch7wy,T^{9GooH*nۉ)*4Bmo8/y\wr#pI rM}!X52o=Sj^"?e$SQ0WrؽD>6> > 2z /w('IE\ʜV R.Bk#ɴwdaSK 6G#oe:)kIgfט%H.@SE8>tVj2Ga*-UpHٕ\OUQ-ds >xL_-Knsg '.R$]Wc⴦?qY*5tx,cʮ_EN:!,3WXm0;UyNtMuWv Oղzy֚o@,R i6f~Fsܻ3b8/deL3mM$n/27UѰSZ3ZuA)%C ܓ ܞNOg7qY/[_pFzM+;LoPh 2{_71Iq"n7ӻ|Q#CoH@w.ES;ATGM˗F0**z2)ۥK6E.R!T]uxxIW4K5hɫ GlY*T0AY,^T9Ŗ­{x;Qp7C;Pi'"HIv1_1Pvhw &BH%9pr|I488Co 㢈?-tY#"5`.w݀Y&}Dpha7(ov=D`cW!m<gz[  X:H"^2;Ș~XJRS"&I;*)@{% {'[ UƩa4lk(0:9aFh :ƆH^OcՊ ʳۓ=3DE"ۀRB$C2$T;#dasBXT<EQ )XX5` ,lUF疱/5.5MTۮ%),nd+=d /_ڿgy3VX0&@0?Fo*k=T~d=atVHcJ. U)75K']pE'(+8]f݃C=JB;%"؄\ o]6Q:Ҕmlr4b\x<PNb`;WGb5-HFCDEZKNw }Wxi:NƳ+k)E]MȗљZJ;QlD"!x A:zwkmA,<hAFJ":jf$UW?J dU:2pLFmBQg:xgAzeU?sR"Ӛq|6 l3qÚk"be1Y/!jJ9|Vt@HGKh_Qw!M]GH[t 7]%}DB&p>|^J2cwY[B4s}`d6 oqЏS>sN,1ЀDKl! Kk@IӡÕ{%yO&5%x?wCIP}A2Ԏ"?{[GL a^oUދŚ*d@*Om}NUi\i~!"%q&+Cru]V Wh"p[vPLS# [d9u'߁ic9;Qx-eVuA):<ņk<$?>\YgbDK](Z *Qyΰ5Z>_l:T4ܥ[X_@WaePvxxξ=d1v_(u6sg\rЮoA%O8OE[Lṍ_J:OGÏhg_ BYOR5.O+.nR|*3A_NvJD"ȏL\9: 6 [snb'P5/(QK$$ `mH/dle{YJ,NKvQt泥kaסPWmDBh㖭s^H]<[TpMQIDzWB1cE%GSH[vjwjQ@0xò[8bJm)`, Uy~707x ukWƒryD-TϮff'uՎz .A7(#bߎ\F#kU(44z\A̞I9tB(/K2Ϋ9K뤿/}n!|_sFmHӳx%Q6;J/paiE(&YRb3|*Z<kMQf E@2~5!7l.֏fvD[{ƼvlM0v2|*+$OD6Os2iNE=#t},N<6@KRxPfva=ݠ0Cfd~^B[|iKIpx\DEdpW}WGXO]uxnTuLqn>|G{GL7$1-C9b(8N0c~/]5>aBO;a\)N" ǣR*62ϫ+,7[26]4`vNNK\xN|sRKE[5K5%kƽDKs|t>`N.PȻep\i!#(IMo `Wr mTDֺFA^e9v M P?Bz:H=@QF?ّsi'PVT~MAƌ%wTC'^U) VtS#DZÌ 9x(Bt\|t?Phj<܊ @&8HWR˄ȌS~mF44YŠo7IAhvL|);9ӈkϠA; YgۄvYQznks=0t~#L^tIv!Gl02:%&z.6mi$hj*R-;.#<}2yC(grKȇַȾٻܨI/.4f}Sڱqm/Lbұ$OB48%3oA0gVg`Y/ERjYڙ*\cTݘTSQ_LM1}2f5S{S6EU(sC;uz;_f<ģ:Iy32n/[7m: E-,8!q .<$+>#*>{tEy\u(X$6LpVn=us}Mmn&XMQ\]I~B8LivsfԿgu{ ,DmHuǴGߊpJI\TӬt+\lUg[/Vm:2۷)GeW(]߆hkr$jbi4MAJGIJ?ntHPR/}ks零Jx_;B7-[e- ̰OSWf#'/1x- wLu/b~O5UTGlעzZ}J)x" }mٚMQGG4jCg\yFoiFٚ;72јDH {+IF 5ު<>Ҏbt7w|ҝO;Wf5FJ_藍x r랑K,KA]3nL*???0LK͙rLWmy6$63I,x}iX/ަ="釽eׯ)D6pӜd%eX*6-(ڷ"MCyA Dn+֤mb:vK$4@=71KV6)'%?c=`*5:6HFmArD618Hx+u=Ҭ@mʠL%$?82eo]}6D2١ŔmW^e{a7&TQj~y20(2d|A ss Eʱ?Z>hw_NDž "1 F3'e׍ommS`DCE⬔[kt* c]_>5fF:Noũj XsU'I\DZK+H=-pGRg:tiD(K^$-p.:2Li׹eeDS YXcsԃ"n>_^8:D=ˇ5޳圾{J G.,&N Hm6<دc׉M}88u@1ZĚq_^CG;cL60AJwWYk&;9 e)ʖh~*TsKɧqhdNɼ:WVf!N;* w& g-UkTr|4EY +g= R[f ɠk0/$d{ Èx'U`5JN_Dqel(5d`p9#@aweH]x?94`C@\į\;l7<%>|oMŚ~9'n&h¼1/wMzT5kr~vFǍ:狹8,c 7' YKl#6k&GT! o/<(4'؛H> Mԑ $f"$N4=~gV^qfZ(1jyқnxe@:y2DB. jr<#ڲLI"[k_w'hR6O@a/AM<3hvyU ! K?jg4ؕ$=Y2|&FS.e_g^t*m3 #C>㦑׺&2d ^7|W3Ҡx <6qv%癥Lơd^S By&a` AƠ+QJb,w`Nq: Kڃf 5!phLϊ64 ? T9'G9_+YB䥲@*}G/[riE8p )֍l.FLkѲ _vir)ՖMkԠT\C!֐I;}yuLf&܇H@v~HB ~8qBHk ȄY\W8 bMN_ݰgqT$|&=w~FZ5$cH|IslJВ%T: 9OɹP֛AI\b42Xڇ`]255}![TmNʀ3,pqEq׏չ_,sT4uRZE4yuL c v쏚F&%Ӂ)  '[ej$[E)Pet'5viP΂KaD p 0b_HˍLϭS^[4jm>2O Rm't Rtia+O>(1§, f|ܠF 6$=R<5w'utwwh `^yb-&x=/)1.!!G:WO2OPm<嗲VwTDSTM'#kkX xzoMxPٸ QRz8YvȀ-|Ǔ5doޠR ThAـkDzKxP嗴ϖ`ReH>E׏KNTJ`r㌫dA+Itb㕂rJ¸ n8N^mLtU>50ɒw|۠Z#6[Kg OۿQAYك"va?,l7XxX&[+kƱW;0aG('~(h1[I>ENeMmt5?m.ꌫkCbN㊲kB[h#{'qPμFmW@t_Gd#~;nFL[_ik~".6WvHT)%v+FO'zr0>Д`kqL;ȍ Bnv҇'4hf ReI[MzIu~*tMCc RrގoK]Vt_j ۉB`\hF}cEñh$d-NuײZxuRdGw$Xm~=-flc;ʝ!.E<~s)KNr) ?X޶\K-c`[n[Um@0=7sֺ ñ7^񂪓IB/!w3ϫWhq{+Oj]:%dBY% S0žE*eh%ǰ9ZX 01s*"J6Z,2d۶s&Rq˄Evnrlg1,XsیT341*,TP-$KoB:iS(܍A I{)7eFcҐ"eul$F" È:NBb SK#[0G}SU8@l@_B~rjه>I,5ux]U&:[WԷ&F5ݬdXJag9n%u+Wg;mNfDi؍ڋY@hCOVß_@`b Ғ7f#qvFcW;O"[uƤq=Q8n|_qQR:4B%Ts9}oL hQ ńD4A fOd쏭Y#+4Ʋ.^uf^lP\h53o  $wb Jr#xplS H kz/!bx9aHn*lGց3hZA5RUѐ{A}#߇aC+LN)ժH:6?55^["ږ,eqWE|:k`f4~)M^ dR$K:5t\6?8p@Ȗz *"$}<d"qY-K1%t'0UKXD˰4+\A| x5?C{(z2ΓAg,XuB&$‘rvr7R_=aNmnjEC ѧmjbY43yj6}=N"d/><5@$J"kt]ynϹ?>"Ɨ%V (EFL v=J :k0[WMQW]1M]1ĴЫaL* ں1v\E8b*F9 [hhEz1LWl2ol _ IV}̱|Kxc@br#nrY6X=rzUʅ<\?5F{l0<ez@+(t@2+w:ZE6 Zo*>!QQ²LәW`eGA'գstFo`”ۧaƋh UX'U~'ʽ%c1|ql~1c]Kȫ= }Z&'1ԓs IwrnuA_if}c2iwXq703A` x S,nfj|lܗLv0򡕋}MQ%2rG;5m3zהډ+@w͛5FGW&yvrd1hC6`eއxѠ)wV[,:uI@lM>^T ^i_>O$W yH*k;od(  8n0iy$,] xcƈ1>ϔ3<#灈>J ꅻĻHC,GƐPA#}|NsZ|!:gK%iưXՇ8tfLP1 0u~R0QZ\̞fý-:YDd  ǖŮOyp#[Fzr٬v_w$Ge@n;| zQ5ҭPw:+ 5<_:K3|[ovl70wʜEBt0:8rYae期!yi 'Uju*B)ȵC!D7%r|-caDքG,1 TP@M^;Qh/ݸwz\ty7TD21⒚8%j* {püU(YlNN? O] [yZpZ.;u]x?ON#6O^~0W-v} U{QAC$b4%2T(:7i~'ސT'#c_\XhN#⫾3zF遭3F &lb~olB-,,EɆvR IOGpjb7RxrC$X7Xw?2e 9 Vp&;~;"'vAi?|##8jfX2CdX7U>U,kpO+dkLG5)}EJ-"uneShXi{ٶ}*p#ԚZ $ad6f)KV3NŒށ1%0#+p~]V /}8p],?F Tm7]7_[} |2^xmAuv!qS+j\ 98S1vRΗԛtasWbCLDm[2׌صxSXA TQjq>ιOr*vse=CΏ>RAKqe%ϛ- 5b?c9@ (\ ؙI}# ! 4Hg*`'դͳi%S`H߅bzA%&=NgR} SX62aK\ ,o, zxݺǿKp*ޅ#>j,2}@:֢ "I/B=h]M;a;+~Czc})1 rZ^dL l|d;I$_H\$1$#TV-K=CQ=,pFG[84RBD9.IT!$8k[,17 Ax2 ͔q $kKU2$m Tv5{zMu Aך(іW.ʘ2Xي [& b\RNQjП#nz 64zNaaYɎRbPoRLrCmܥ@ !R\ѥZ1WfFy)1n9Mo0Ʈ\WnEw`^hN@% D Զh'`c6.Jj3p"Y;|b9G kgHHHºIVfF 38FD 7_YGsau$R2-r?uk UrzFq RAj쫛D??W w3^dk1}@~PSc\ Ӷ(XZ #ZxTEj> Ѭ9(A.!qrK3uk\\ ^RGCZۊapUgʹB[=N`vaBZ../[L[?Ub@4xcgTmq߲TDyӷt6; .T=\bl2?)D7$ݴ˯v3:[}JEi|Gx9y 2mX՗AU+;~'hU!j eTHՈ?YDHRIAvO'xӤ>isHLrHhTu0:arj47~XkeڹgW[kW ̳9An#&e;wVloYZyEi%䱫z7!^B}3:2TS6,lU|$ 9dl69 J$ƵAơ1dx:;IlQih[z0 ?6ؖ, S32[mO2vVw1  oa\$PLW?`,5fRC)>$:&•wbcyN[]>_*SՁIZDt*/=,|.tٛ-0JJk{2gDcO[84u:7 PIa9B(dB,pfuj&kS[unEeY4]?D/՞yXn(?4i+X"rנ7MEݝJCY1: 8<=" KO9W&44 Up<@<';ɛ״^d:f/XE*}ڒ+:'?|!'p+"-e~cЕ2%67殃=Gj{Tw$ݷHA< >"lms^ћxyf ™ڑNHx$!J`9ƙ8G AY :ۣ !]˽ '}"gv:s/gԊwEm\,ظ|v{C~Aq3IgB[u `;<{}?{Ea -$%G˝`0B~LcoL o6 _"'? K|zQٴNt]cW)D /k1 ZYCsbYy} \]e.ŵGQA5=RO(]4U`'rUH91h2ݛEGlL.Ҍd# T>n.U-ڍ1C'l\J1FJ˵Vj^imgڎkpy_ogI brS`xqN/ztcSOWFJX{"Lb7$,$TRC4E0I\#|(Pj-8J_ ų FZn SNÆPߧ>uo r x6VlH=1-8 C>Qصia}ݓj&Ak8`N*W[*<{xa|xFAB.\7KJN|^2PU"7=L@m8p-EP2kHh9~IVgup_n1i㳸h '1GJIݙ 8fxȍ+h1ZTE%' D0q vƗNsO!DIyS탊|#Izax95\D*CvH8v tx7luwY=.1~0J[P|P4!jI"Ĭ -a#\ q.=$\ec"67-.>)D|1!cccBy,\Q)v4Qa ʲO~_ XE>7jᣵb)Pzz_Ruy\R5 K`пg߈:Y_O0i9Q4^>a8鈵 L̀#k,.ٔ^*_0{M3@QRt3[K?zϘlؿg؍Hzʐ1IǏ̺ =@s<1nFakY#nb&]LtW!Hz)L~@S 4-e2G*W,)w;P+|  4D2EE1k? A}^o ,6W*] eF( hAr]Ja!7呃$F rmq>W}w>h pKU_S h{Ӏ l7pON=.C8 iK s[hr\U_Iņ}nݑ=qD+:**%I#:Hd8SqPݪ~JcŪy +c8 7Ϻucs Ztc5@rΰVޜ7o3e F f%9y2RǔZj5[Z7VQ0{N)Zy"#;F`}K,@M,&LL̒Iui0-wB#o-K4;='ʚ?$fࢌ"%¸'4vܼKtVo7TCH'O8 EϔIҐV3|ow?@)cmk+0 V yJ*`ĺ/k_r9eaa.jp?#{9cI'm_M܋DE=Pݒ$g^֟=gNq{+cB@Tt~d[X(}3} nIn+\aaㆽ*il}q*NZrrA߯J1,4Gn1WY~}M #ji#X{;>Rax2{ P*yl X  'DWx\ !$S>8es=R뱍-rx,+jS6F.Qo W3h%*^>7%mG?<YwIߺJۭU'tcΙ2}ha4ȣa+CAn|t,*MO ˤ27C2Vdk60rR!G~PXmezpBbp(;zd,Eх# ^ňdcDb[-M~P գr'b9k% 4}8rlH(l_%|P1(L5l=Gq«J _$K ET*MO9ߎ|PItόq٪،9A;1\wxZyD#k{{BH z{fJ,="f p` B$\N|z%SaS̙L~ M2Q[jLx1H~IɀG\'v1)RPTo9|{AO%GzzszbEY. "C9EGYOTށRNQCoG ΁+Q&Sr;m_e ކōBH ~OD,l %T3\Q϶|CznХ/*+>_ eCTg&8sk~,ZAP0d|K+JZ]W~@h^Ni1fH !Dx| jB`\|}?vљCnv-hSHi赩3L޶3E`ߨ׼ ETfCNKPYYt/5 Y'NyQH/,FdS!L%`<jY )4 /zhi)考;(o[@h'uUZ{6~gT~%G.GoHĘeR?ɢb!j2P=s@6v( $l+οbճ ~m"'<56?I w˥a"ߎm d 24^27ۋDLdre|J4TvRץf# l `ϋ#1<<;-J{멋`;]QZ$JSnkZMҼDi{~BT!| 7˒ܝ UJAGVX.SE/VK AVj@X#dSU`ƨ75EH1?aKhFhӒ j<3ϼK 'C-&T ~JFd F{TN$*[ݬh$q$ni1H z0X32kHFs2!Lg\'뎗B萷[qIeN.cuM"⋫7:}%z?;n3gg.M8x[ܓDݔ"M~ȑX^4 d|,Lt6Ⱦ-z*'%IyBK5q#ۖQN> H[(Œ\f{6IPmcHXJ#r?nGJӕ(ƪbh7>E52@V;+ᚣgTn`lטA9rJ7 7;[<ٝobKO/M=pƸ ߕ-6桲x$~i#q QQ篢֍#>DlkT)M%42MSW,,NEnTB{N;k5>wۛ^ag6 r$-:n2VSV%j̜jl̺s.|yE4y,4Ws)IvBwca;t~ 1aeFKIbMgPbQ& R{ϯz3bmilE^#`tE<'2BmIB-tB5?t?[|T6a@d1wY1 _сwUE>Vf_OZe)qc1w'GyYX('̠3;řR@O׍H gn  H:|#¯?yM$j3H؎:ٖQ#DrG# &pH hdT)L R4#R/tqi:=Z?] =~Ki iϒX[28׏t)lx荱)Z&!VRM]w!m[xMi`Jf}Ơ;Nf?4_rR[,vHn\x>ʒm x[h_g\aLAWhAF<ˤY֠nЙ!fEjmex>tWz٬![[HZ r֒PiC\*wg0uӻ H(LvPp TS(EP~]/wMĆjOʝ9+*t8X-0>'+6ʑBҌވˡ۱m?/( ~AJ^tsX3Ax6ayPz:tEZFv> ģCƑqKX[ < R8qG隃/|fh[y e|]@:W;㸐swcX_"Ʌ徛h/@=P&Z@X q|P$-:cP'Eu\9f6Z9,Eḱnrm`wQ`1+g>Cilvq!w#v/FxU3,+F wM\0He>/'Xk-= 8^ e2b ~CVf*C(|^2|ɉ1\ 0Gs{r٬_;B@ݿ\&_߸m,X[|[WCI|sY)%ZXLY ^i+]$}Fх\9gf,#U΄aO} ×jF5ra\m1)U,>DPgizW{84)AUR , >wMԱL(; .ګ:H^X ,v\4wR'62 πS(՛&HH ioO}Btu?/.QM1pTp8p|6k>6 -}9XW]Uzpp/3x) ^k*+!5=8LE8un5M~RXy_9%{pl6PRGYQ:p}26`iI)ujӭ\6?hib`BqWHkHkƯwRV 8]K;"ČqO/.pRӯS!3TJ7RZ.0sU2 (W+y [ZqΡ.WKQcE}ګX h4MCݕqx[ ) g싘w-+$vq$޾*Sյ3,TSZNcJnV}^DvuC00ffistXwR?棷*X%uClK`Lr(W&^ wE5v[cbF ϕU&fti]gJINP߶75Nkܑ}yЀhū=I? $[J^Wʛ^Ap [ϫMΉwhg%jhL=3(9KhhXH|;N$ʵ`dlfaEf2 0=ل@0qךnb] hjuh 82' d :R?Z H% Y,vBsMʠ HvלBw8 D?" \qBhiw]y.8Z);~|=TU9=? x8*DHx@=>|ˠm~j?)Ƿ>c-7O\ ;a3>6N6,+rJ. q8C~a6]fQў*?%Z*@|bˠ-pO3nFD28>QrS2ʼlEYJ߆='%n~[\7q #xKյ$oU$Sw|;H$y% ,ܲ&"K(Kǟu Aj .pFDɗGx;$EcoYVXh 1ט0DIel;lgq-sDkV:?p͉3++~4i^K|NP/u8扰aQ~%P^GYAI71GL0$ 3&.cieҺ-DŽbLQmH'igwJ/l }%q22`M:P#ciz==vs/ guDblU 0=~~I Ƚ&~=č;DpNE7o6a93Bho c4ϕ7\$}֦rreBnZdwE! D>k`A٬l6tb ~zqЮ;eBJ \O~h! 1C{Lk  $;#G>> as,ph4&}-ěIgP֓ę4 JZqFnW&ppJt5$6 w-dy:ȋ ~ @(dwЊ!N%))2S?҅.Ф+; ^η}@un>8n*6Pyɡʭb+9ŞQU|h3p5-nsņ= y!#좆I'l$l&%m`E.ڐwAƌ3-jeA4BE.72gWr74濞 Gli+,<0LLI!k4/yHh89p= J)=w={(O,Rf-&'~PСs#̦w\^}1AS̎>Y:ual>C24VZ(C*Y5]~Df|pnj%xI jhq L0`s!H#pwrAحX^NՎ\"_ocuE^e.?O`gGvRV#uL\̚xߓOqSUd;5xܹգ:\kW>ŦIj'>#%0OLO:P-pMD*ܳi#1U!RC)24LtvrSŊb~'FZxZځc<C?g;muٯm^tGg`)%!M1rYpkwU=Y|ʡ0~d`]1wΫ-/f,޳Q|.-EgV`O2,Sn"@'}#}2ϱYsvvrvU "9;l(ŞkAJ6VSFpPF0rCl ʊr>;SC iiF-E%٢Y83| bOM?fyJ\!K_o[D+hcxG<-sQ\q;(tXBTâ4ta8jJ)zJAh$OvgⵗŢ /cq`Y/[i/x D*w RF;2k+9gNv C}_A ,s+efBh#F ߣF~kkJ`BTj:^Dg,zǬǓ/O 5! 'ۥ.|i[XZm'DO E(PiKH0S f$,J+o6g{2e_*G1P˫n{w*`6D-i񰀾[Uhb2} P1>yk˷M_I@+ό->v!>+Y^엱Fֽ&EbO 07wP'q/sz(ݤ/6p4`%r|-rwp2꧙Hn8|#>spg)c U$458F8˅˦k!z؇vVEs%BQ58GK.z ߦ$'Y.'FZu}&)HMm0SBӹw:es>x- <~: P Ppօ=I6Lʣ"M*R, ODG,ԍ'g"G~#:9yR_TsQUʉ̤K%^~^ CZMX&/|=d0EY2B)eSK$aߟ쉌XEj#$nE轁nCe'V/2* zLHZy)i!G{!riԎ>\=a4'DZ4/Κnwo>%}~^sǂ D!}$M _AFO@N0;C6wDNOqr{I_KeܱxSo¥zIwK{2|:;"/qnmvJW= u60u1nE9*bq]YA? Cb/6 c nCt^2=B6@VzD[NND{wzREiPa6{ICodH$ġu_8[L#mڪ;Y«QM-BM3eVmZyz?9?J -+#Cn@Jz`gj N7tJ;Y7j϶ؙƹsIEXՋkga6ٲ31 lcaĩ/th@#{esT_3BБ:nTA 'dLt\M ~kioCq<xW}!vC(_v>R'`$A96@r~GV'*\dO/\ΓJUz z|6ٻWp!;RT/(Ռ)U1L}ۢ_UPp;k`$ J7"C?bTD4s|e2\~b,'_xjqo*"0żF@X𝴱IEOp}|#NWS,шبNj/VMXYK؀cu=og;?G_Z`g4W[⋧B˨S5My C +:u\ͬ>Op`mHR‚sU tr1i01fk}a GQ*Β9O:=t7u>R,63^m#I=5QeM݈;$/YI8_:?;YM iz?NA?&vUZ,XZAXK\f6 E>4B"<u%Q*$Ɂ r;ZadZCpSkC0J% -bi B;$vPza S} VЯ<8=ϊ~*=QC,_ƴ.>Bw^&i'?~TĽ ֨ąWoLBC%xA+"H!C>ggf}ļU!!:(ƹ>zbٿ;U}ojc[7V7bWUԿu|g#GI0)]@DWp'nb@E(hvIn\uPJWFjI):8S'K̭>g3$SВŞuF y6m,`l~.|T߇#Y7(ޫ|%fODrAX,`¨703)-%Fe5|%:D0}IFY|񑄋,vm !i~|QE|'ǴPy! BX%nP)6=Es嘣`~3 cr/3]u| ~a;CTss7. n0bd$#ĕjEk@cQ$[9 "zy!Xg'MoR݀zL{,+P=ݐu.wlq7 l0'cUZ0ki G6(Ctz "!4zDȔQrpW*/BĐIƧ)aUYX)4%`>ŕzQly)Vfcս n»xEHj0,ˢ}^Z^_r&{,LDYƘÉiJci1!zCn棬U4ĖX4]t0|0J͠5𢇺|wG Cz{Xh5XWds5,$3@Uƥ4 x\&F=Uج]F®ige7zPHQX~bd uPŽgjhUPʘP{v/<- 5o7[ -&v(bOU@C0Lu_1 ˥JGtqMH%V@Apo+iSݺC5:TW8<<1 >#y,P)Wczt8Mةx{S#-ׅ݆^kHsbYK+Ld ⎭1\(eD?͛&^bF`7{6b206qS'&ᦱ22~;PWʜRO] r qٴgU? DVlYԀl3/vpI҃qv?L@!`tgS`j[ 0Axz.e MTN%CwfaO= օri+f\tgw7_mUGe7K2[aOK( ){NJaFKh+V \beV i+w{hҞpd%dA'x#[tlw ݀E03^U*՘х7#f{Q;ϼoe֛K Eֹ>K^^bP&2 VXP`{r @P:~GK/FÛ8R( G6elgXXPN6 *Y#9_9 !o>)kc_t=L10ȜઋJҏ O PNk 3S=Ņi砰17iftl>ƅ6}{aLoyz$NYO}+X ɿ  E{_+ { ٠5eljgq%mGX0y ^噵E{Sbn j>SX߫CB"i:Z](IitbUS*ayB֯P`<_bQ#4D Fpcz0أwE %Ϯ2EE>׿?~&g.6s6l6Ϡeʙy~S1 qF-A&?x}v8JfbëNݍ A{o9 eGnB |O#(͟ ̶MQI=*Ig M]gtT\zvsLWs@vz򸨪8-rnBKUM(!wuL2YOvN-N.*šeBXµB!կDPll%}9&3/tYߌi-@/&|boWXqDv-LD IN4e81Q.3Elu쯉s+?H.\"L Zohg%rp`OX:qbTBR)%97\?+b*IF6'+Tj: o)fsy>8{ۍ5/NPU7mx*TI^j71&xS ,7?]^QfR:it|Tw f$4q.jg,`4Nt* Ux'Z'3렵"9ʿg@t&{JyG$]k5^R-g]EYNUJ,.` ʶ dl͹\L'|ħq*Л : WgB!%/KN3yOcV%`)¡45n|>Ss%M%vf{qDa>'Vfb -5 Iô;q<$i:w\5ZS8Ac'?Qj=%$IlvҴ`ƣQЗair=}t8#9EY`a6o .^<97IQ3+&IUGD􇏺 Y~%]_s >\T+8s($c/lYo$UF?㥓&9pn 6̪Zcb14 odK@VC!m;Ř[ς9JzаUAY4>㉬ ڲ>͸ Wt܇I@[ެ"1p_%JlnwȿtH}B#9ۅg965:A5:4:>skw(=_F/J¿L{"j+_x)t{N{/Q㹮 uzwip -\+_+Sj ٲSIy'6h\Mُ~h;.1֖TRR6b/W6&l1/QxRdJrʘ m彟?%,a0|͊n#(c^*e@YF[إDWO\NN&I wm%Ba[$ۂR/JUȨ0ިBLzPe)ѕ炠<=Rqdw9VQ{r(=tT9"*PP|sCb5@r" GUYUFd[$ma/0]tT+mrdg HW1HeM.Ft?;"u5I]bua,MzftjrLf:L}XԘ4AaC 2+t65<e=gC3DY`@0=g5 ,T%ĊE]\=# ZHO ~L]F>:(Tv!M#}LΨ3lܶ'/#R:DvK&V}洗fw34y7 qRh"*w7ũ5~fL,sE-mqˋH(K;0I~_M`,\;jk$ɱ {<q(s~&a=.h(_:v[3p<7`4sU͢۴LDI2ŔrEMs`Pb`] uFPHG~YJ}ܒβ|%g xA3)&BZJ T@KF$% װ{]0-mJG&KZ,흴3ң(vU ,]V> ?Ym]6[,y.K݊Ȫ໑>e袁=ZwXvXZRcӅ~b19="Phw'.el5qDv}0/Au{bvMF\6.*yum5h/z.,0kst|5`.>jѩxczE0`J/M 7SW |( / S =-㧛6@0iұ3eJeN +)n@Yvݞ!Da2=GflgMdk>kz|"ʧ8v TG j:V =>Lg@cA}R7_hJ|hC}Сh+?갯i!^itaR!/oE]*jA[,5ܼ}LS)͊Yqr4>&i%L5탇)ӥA~ny:<Z ׋@TC;+Q:X5gUqy@M *Z3$&Jߎ3RW:"D^ !-pIˀnvE37}GYD9<hB4=dK{~Bi {g1zW6#JL!ˢjrK?^_۩ȶ`zwHJ0DyFQDN^N3NV >Ql0<+(),3 .^o2}#EAbLmf…/mn݆ue!)g.FnG6d] /B+hK*<51Mg4)8 "2xl (:eݩY?[Z$#c+mXc[ -J?+)ʕQtOxȍAVn0T:Glb޷%d7~ f"R8I9uNeMshBϡ듑sΨ9rgH3.. =ԭ$M.0Z \nڲHO9gO+!YW?87b)̔\)I(i33uUg')z%@)s@tO ԇܻ/nj`x1?3%xXv/B΂IQbFdS8>,w}RG\G"3U2`>ؚZ O]9w9v_9H*{m^ΧxTaشC <|fJHA"3&ճҺ _e HػjIs RoC{ᜣ,JHi[Cs*pE{.KIT UduSA]hl7K%\Q(lser`*dۣc-g7%8[πk8{hb@On&JJE]_n@P`u*f+G,UAQ< !T$pg]PKW[r7bN(tLgs=4; (ЮBa>By-,@k+Hc$HPM?I5CT'5RbA0b%cBY]nlESWsH:3>X܄abHR5aCtv̢j\XjPO+lkL>ڶCz7|hDYh.ĩɆW5*K*~CF\R!A9+:[eWB1)} mM4b{YSMP9D|µgx`7Yj ai"ʍ{;'TѲ q{|T5?dA)]S.\0®:?,40 |F uCFEWDeiX?Oq6`KKm8hܼkˡp9?.&iSZȿMqE2>sVnCSPEۦ(O;+u8 euBiY;q1f\FCBn֏=I^oF[`}MbB]n78!(nN5d*80$"[16pdYX݀ ޲&t|=%EJ@J:# (fZ#&&ԕ㢟@4Ay9sj_;:w^QF|]C Lh&rK5 +?,wmja$䷎=S"Hs%>R2.8n F};{Q‚ *љW;jL֔};ی.|{gQ}d cTt,M 1k*Y fnt]c̴O(dZMZnzSl11Y!bQ}N"b9smګP5yfurRaTAjEZՔ`IKyEn=YAD~VtNnKõ{y6U5dƭ}LmS;ئ6E:  -銩7["p >izĬh!*CTZP&>ҎgIf-m2ʍ YԷYbfT)PmR@%xG>7O _Cn^O4őbI59)X64?R8us?D䮔)1M .>S^3It'?^Yp 5aqj:{:7Y)koQ/i _a53O>Ob9U:;vMZ/:\b)*&dCrO3ju{춵'[[e<% fe9Ѳ@3}3,Ssʜdy;Xo^k\ (eg%$"Rѽdt;:(رeU%\o=?L=(7XQɟo4EBx?a~}=vنpI+·Ûw) =m hҕɪl[h}Iv'' DW)^{/gtҙ&7%r,28I0ڝ|tǴȋ!R] 3A|DK&(.Z,QӈCϚrS mmNC֧"̓1BV?sxf?A͹f-[jEaMܷBC, ׅff (YȐ57ŕإϷ0_wCB;A?ωX1ҨՉL#/O?[4c1@H,E8N{Z8 "/f\ -dlr>K]@^>j «P^ǀ!)2/qWdڈI[s/S_Vh2:Hp6S`m4PwS'P(2닐6/:.I]Y-jbWx6SRfyC#t23>-@'6f9yF2)xf[@ܩ Qy/t(Q S^ڽFsbH~l" V Z&.U%u~D?`/0} ԠҔb)7$Õ}*m%ya jJD`ѭ %?h̩މレ6[~(N ľ'ւ>FtwJ{ tpNJ~-e^v :_yD~ qf}`Q+7j.F,l*^,62Uϧ޺U;ӵkX2}F6!KR%zi=ZZ;||xPu凒ANCOU,z zCAzUmE=i o8H$wO STqMoIXM 2J %iD+y:Ӷصp6<|Ss <(Edz?ڞ5oLTVEb$pwq0K}c !L (cyBB}V\&)vƣX4y(R,2'峐BE=kmyPl"} eͽdҏ46(P)TҠ1zrXyUZc4KYV)d|xMSRm_3&nЧ;ǝP瘁|-{||-+iV)}LZ4Ԃc&3W}~ɤP+JLxtK6BoWTh (Նq<׃dzLp6N$2Jl6#$RZp䏥f}_Wؓ?6lG݈|Τ.5LWQju|Y>q}0vP`uʥyͣ3r9?DfvQ3Jb$S?eg( e$<ÂNSTktPа-1C$}8z 3&iusmP &q7t˩e⸼JIZo]C\O"xʿ}* B_9&vf뽡@U!Ր̄T>eU,am$#9,hvYb1[zGGǓu$l ʈQES!9:V$W`' JuJy1}yN\VYo\HX8rn,6+Fڴ:a0ԑ o.aGn|}v?yՅ9 %@G]E y(ʷΒӝ"%0ŃOB>+7 uqTݾ{PwA~\7Yf%ۦ+7#/m&5 ScAr8ۯ͸ ]F6JɎB$uKW ^F#ȇ# Ϫ`+>I0bA&$"Awi|VH((a/)q\}äGsIe"<0溘5k=o u5)t% t Qi hS' UWϡ/$|` .H/ׁX^ Y 蛃L$~<N#5% {\vya6lBw8mE@p].?8g Xft]_ZƧ?b Y,~;*yxؑZTƗG6dw.elVqɉ$+;!˃_b|dx a;;in u\Fn'm@n:oHbs2Q:yvu-c2맘#A Dw,m9NEsF!EN̗} 8%D̥e g]:jC'%}E4Tl{\PQ[ 'T]28U8!*6'4I=_]NHA{.7R8ї{RPf}s3N4\/_[O\ mQd"h}=[04¨0~ڭ' (/h#mgv (/فXuE+/?0`$GxC /K4 u&SJлN˶'6EL~-몆F}<NAͺ@P[p)+|8rYTȋzhE{u|(D*xx0o.׈r6\'D.d IR~ob=Dܪ xǶg6wuS 2lv(V͗炫UҘߗ:~^ǔ*͌&)geYASJך0u6()pp)~bJn}QeϾ^hmM 1=FJ(kbf6lx/1?W%WdX38m.{sZAہEXg*#dlޒ2uxc۳%lLwF NGmi/zWEVWRqlFd2bB!:ц6/7߱"ZKX' ⸦ bHGCJ[Sa*0\(>䬇pEmA3 `@@#$z  NI\y_=Y) {r?f^ORu%UnK_'1]fD rikxIŒ5\N.O1:O,K6HEU9c{iFI$80.L+) i92h#rJ~ i! [/>x1H0AAԵSCغMQ[BA? =-Z_m>Ub(q GWV(Gzk'/ѓtU-@XfrIPJg `Ju>ETTzU6H)u%v$tggl5b[$'K]άeZzc't&XE<ǒzSˁ c[lK\3D|e~Rn7ɛL_ Bq n3M-Rߏ]@|Wgmo\\8v~-[ }g4u&5-nXղQ̾ bBSfS8+!)r Ov^%3/ yMr;.LnUzǟD RH1eޚiǼuΖK%?8Gί3 :iL+o.vya()])SP]#s΢#8厇oJm"ؘQ ()|zM`)K&CvٸؐaC/=TЍ'd+k~wӔ%7ix@Ϧ> :#)2JadW4*/%KB2|̗lYNTuNEZd G]-w}N샱 )?mPhR1Wa]ʠ2IChӗ~L_-&b}J5t|"~*zѾ3sm:MP0R#"_*I-Q6Kˇ ]LE2 D$z9u1u6%L^9=)A;]zqN_2u6msCZф'cTH,':G t'  o򺩲&u1f\n.8hy *G?6aB e[.\I`޵c' 黀+}Es.r^*\ؕ sCT8u׀9s@g!}׭PqQSHFVX.)^Xi~kN&=~/{>6S˅i<7AJ y|l52 ו v !Zr-'R(Epg`M XmClDSJ=댚.TxAP^=04Ħ!4mVث> obBpP8|mMD]T9j/o嘆eI wwcrb")uzEWfk[`CT4 tvn2 [bDŽ4 EVfS|b3 2d$PQP!Tbx~