From 1cba17c91cf5bccd9b258704c9886030f25e396f Mon Sep 17 00:00:00 2001 From: Ying Date: Fri, 2 Dec 2022 11:16:57 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E4=BC=98=E5=8C=96auth=E6=9D=83=E9=99=90,?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E9=80=BB=E8=BE=91=E8=8E=B7=E5=8F=96=E4=BF=A1?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/AdminController.php | 67 ++--- app/BaseController.php | 75 ++--- app/HomeController.php | 25 +- app/admin/controller/Login.php | 71 +++-- app/admin/controller/system/Admin.php | 23 +- app/admin/controller/system/Plugin.php | 7 +- app/admin/library/Auth.php | 53 ++-- .../middleware/system/AdminPermissions.php | 40 ++- app/admin/view/system/admin/center.html | 2 +- app/api/controller/Ajax.php | 13 +- app/api/middleware/system/ApiPermissions.php | 27 +- app/common/library/Auth.php | 155 +++++++---- app/common/library/ParseData.php | 2 +- app/functions.php | 46 +++ app/index/controller/Ajax.php | 12 +- app/index/controller/Third.php | 59 ++-- app/index/controller/User.php | 75 ++--- .../middleware/system/IndexPermissions.php | 33 +-- app/index/view/user/include.html | 5 +- app/index/view/user/profile.html | 15 +- app/queue/redis/Push.php | 50 +++- app/queue/redis/Works.php | 41 +++ composer.json | 3 +- config/process.php | 3 +- public/static/css/center.css | 8 +- public/static/css/style.css | 6 +- public/static/css/user.css | 193 ------------- public/static/js/common.js | 261 ++++++++++-------- public/static/system/module/admin.js | 85 +++--- public/upload/developer-1.0.2.zip | Bin 94985 -> 0 bytes support/Request.php | 24 -- 31 files changed, 668 insertions(+), 811 deletions(-) create mode 100644 app/queue/redis/Works.php delete mode 100644 public/static/css/user.css delete mode 100644 public/upload/developer-1.0.2.zip diff --git a/app/AdminController.php b/app/AdminController.php index 1c59c8d..778d1c1 100644 --- a/app/AdminController.php +++ b/app/AdminController.php @@ -11,54 +11,26 @@ namespace app; use app\admin\library\Auth; +use support\Log; use support\Response; use think\helper\Str; + define('AdminSession', 'AdminLogin'); + class AdminController extends BaseController { - /** * 数据库实例 * @var object */ public object $model; - /** - * 是否验证 - * @var bool - */ - public bool $isValidate = true; - - /** - * 验证场景 - * @var string - */ - public string $scene = ''; - /** * 数据表名称 * @var string */ public string $tableName; - /** - * 控制器/类名 - * @var string - */ - public string $controller; - - /** - * 控制器方法 - * @var string - */ - public string $action; - - /** - * 控制器/方法名 - * @var string - */ - public string $method; - /** * 操作状态 * @var mixed @@ -67,7 +39,6 @@ class AdminController extends BaseController /** * 获取模板 - * @access protected * @var string */ public string $template = ''; @@ -80,49 +51,42 @@ class AdminController extends BaseController /** * 当前表字段 - * * @var array */ protected array $tableFields = []; /** * 默认开关 - * * @var string */ protected string $keepField = 'status'; /** * 开启数据限制 - * 默认关闭 * @var boolean */ protected bool $dataLimit = false; /** * 数据限制字段 - * * @var string */ protected string $dataLimitField = 'admin_id'; /** * 需要排除的字段 - * * @var mixed */ protected mixed $ruleOutFields = ''; /** * 查询过滤字段 - * * @var array */ protected array $filterWhere = ['page', 'limit']; /** * 查询转换字段 - * * @var array */ protected array $converTime = ['create_time', 'update_time', 'delete_time']; @@ -143,14 +107,14 @@ class AdminController extends BaseController } /** - * 获取资源 + * 获取资源列表 * @return Response|void */ public function index() { if (request()->isAjax()) { $page = (int)input('page', 1); - $limit = (int)input('limit',18); + $limit = (int)input('limit', 18); $where = $this->buildSelectParams(); $count = $this->model->where($where)->count(); $limit = is_empty($limit) ? 10 : $limit; @@ -168,11 +132,13 @@ class AdminController extends BaseController if (!empty($localKey) && !empty($bind)) { $relation[] = $method->getName(); $expBind = explode(',', $bind[1]); - $relListKey[] = ['key'=>$localKey[1], 'value'=>$expBind[0]]; + $relListKey[] = ['key' => $localKey[1], 'value' => $expBind[0]]; } } - } catch (\ReflectionException $e) {} - $subQuery = $this->model->field('id')->where($where)->order($order, 'desc')->limit((int)$limit)->page((int)$page)->buildSql(); + } catch (\Throwable $th) { + Log::info($th->getMessage()); + } + $subQuery = $this->model->field('id')->where($where)->order($order, 'desc')->limit($limit)->page($page)->buildSql(); $subQuery = '( SELECT object.id FROM ' . $subQuery . ' AS object )'; $list = $this->model->with($relation)->where('id in' . $subQuery)->order($order, 'desc')->select()->toArray(); foreach ($list as $key => $value) { @@ -198,7 +164,7 @@ class AdminController extends BaseController $post = $this->preRuleOutFields(\request()->post()); if ($this->dataLimit) { - $post[$this->dataLimitField] = request()->adminData['id']; + $post[$this->dataLimitField] = get_admin_id(); } $validate = $this->isValidate ? get_class($this->model) : $this->isValidate; @@ -226,7 +192,7 @@ class AdminController extends BaseController // 限制数据调用 if (!$this->auth->SuperAdmin() && $this->dataLimit && in_array($this->dataLimitField, $this->model->getFields())) { - if ($data[$this->dataLimitField] != request()->adminData['id']) { + if ($data[$this->dataLimitField] != get_admin_id()) { return $this->error('没有权限'); } } @@ -266,7 +232,7 @@ class AdminController extends BaseController foreach ($list as $item) { if (!$this->auth->SuperAdmin() && $this->dataLimit && in_array($this->dataLimitField, $this->model->getFields())) { - if ($item[$this->dataLimitField] != request()->adminData['id']) { + if ($item[$this->dataLimitField] != get_admin_id()) { continue; } } @@ -296,7 +262,7 @@ class AdminController extends BaseController $where[] = ['id', '=', input('id')]; if (!$this->auth->SuperAdmin() && $this->dataLimit && in_array($this->dataLimitField, $this->model->getFields())) { - $where[] = [$this->dataLimitField, '=',request()->adminData['id']]; + $where[] = [$this->dataLimitField, '=', get_admin_id()]; } try { @@ -315,7 +281,7 @@ class AdminController extends BaseController /** * 数据表排序 - * @return Response|void + * @return Response */ public function sort() { @@ -396,7 +362,6 @@ class AdminController extends BaseController /** * 获取查询参数 - * @return mixed|void */ protected function buildSelectParams() { @@ -522,7 +487,7 @@ class AdminController extends BaseController // 限制数据字段 if (!$this->auth->SuperAdmin() && $this->dataLimit) { if (in_array($this->dataLimitField, $this->tableFields)) { - $where[] = [$this->dataLimitField, '=', request()->adminData['id']]; + $where[] = [$this->dataLimitField, '=', get_admin_id()]; } } diff --git a/app/BaseController.php b/app/BaseController.php index 5db2aa3..2a3b8df 100644 --- a/app/BaseController.php +++ b/app/BaseController.php @@ -1,5 +1,5 @@ parseClass('validate', $validate); - $v = new $class(); + $class = str_contains($validate, '\\') ? $validate : $this->parseClass('validate', $validate); + $v = new $class(); if (!empty($scene)) { $v->scene($scene); } @@ -132,23 +94,23 @@ class BaseController * 解析应用类的类名 * @access public * @param string $layer 层名 controller model ... - * @param string $name 类名 + * @param string $name 类名 * @return string */ protected function parseClass(string $layer, string $name): string { - $name = str_replace(['/', '.'], '\\', $name); + $name = str_replace(['/', '.'], '\\', $name); $array = explode('\\', $name); $class = Str::studly(array_pop($array)); - $path = $array ? implode('\\', $array) . '\\' : ''; - return 'app'. '\\' . $layer . '\\' . $path . $class; + $path = $array ? implode('\\', $array) . '\\' : ''; + return 'app' . '\\' . $layer . '\\' . $path . $class; } /** * 操作成功跳转的快捷方法 * @access protected * @param mixed $msg 提示信息 - * @param string|null $url 跳转的URL地址 + * @param null $url 跳转的URL地址 * @param mixed $data 返回的数据 * @param int $count * @param int $code @@ -156,7 +118,7 @@ class BaseController * @param array $header 发送的Header信息 * @return Response */ - protected function success($msg = '', string $url = null, $data = '', int $count = 0, int $code = 200, int $wait = 3, array $header = []): Response + protected function success(mixed $msg = '', $url = null, mixed $data = '', int $count = 0, int $code = 200, int $wait = 3, array $header = []): Response { if (is_null($url) && isset($_SERVER["HTTP_REFERER"])) { $url = $_SERVER["HTTP_REFERER"]; @@ -191,7 +153,7 @@ class BaseController * @param array $header 发送的Header信息 * @return Response */ - protected function error($msg = '', $url = null, $data = '', int $code = 101, int $wait = 3, array $header = []): Response + protected function error(mixed $msg = '', $url = null, mixed $data = '', int $code = 101, int $wait = 3, array $header = []): Response { if (is_null($url)) { $url = request()->isAjax() ? '' : 'javascript:history.back(-1);'; @@ -254,9 +216,7 @@ class BaseController /** * 获取模型字段集 - * @access protected - * @param $model - * @return mixed + * @param null $model */ protected function getTableFields($model = null) { @@ -301,6 +261,7 @@ class BaseController if (strtolower($captcha) !== \request()->session()->get('captcha')) { return false; } + return true; } } \ No newline at end of file diff --git a/app/HomeController.php b/app/HomeController.php index dda2d2b..42e5cbe 100644 --- a/app/HomeController.php +++ b/app/HomeController.php @@ -33,30 +33,6 @@ class HomeController extends BaseController */ public object $model; - /** - * 是否验证 - * @var bool - */ - public bool $isValidate = true; - - /** - * 验证场景 - * @var string - */ - public string $scene = ''; - - /** - * 控制器/类名 - * @var string - */ - public string $controller; - - /** - * 控制器方法 - * @var string - */ - public string $action; - /** * 操作状态 * @var mixed @@ -98,6 +74,7 @@ class HomeController extends BaseController * @var string */ public string $JumpUrl = '/user/index'; + /** * 初始化函数 */ diff --git a/app/admin/controller/Login.php b/app/admin/controller/Login.php index e9e0fe7..b3318a1 100644 --- a/app/admin/controller/Login.php +++ b/app/admin/controller/Login.php @@ -37,57 +37,45 @@ class Login extends AdminController public function index(): \support\Response { // 禁止重复访问 - if (isset(request()->adminData['id'])) { + $session = get_admin_info(); + if (isset($session['id'])) { return $this->redirect('/admin/index'); } if (request()->isPost()) { - $user = request()->post('name'); $pwd = request()->post('pwd'); $captcha = request()->post('captcha'); - if ((isset(request()->adminData['count']) - && request()->adminData['count'] >= 5) - && (isset(request()->adminData['time']) - && request()->adminData['time'] >= strtotime('- 5 minutes')) - ) { - $error = '错误次数过多,请稍后再试!'; - $this->writeLoginLogs($error); - return $this->error($error); + if ((isset($session['count']) && $session['count'] >= 5) + && (isset($session['time']) && $session['time'] >= strtotime('- 5 minutes'))) { + return $this->displayResponse('错误次数过多,请稍后再试!'); } // 验证码 - if (isset(request()->adminData['isCaptcha'])) { + if (isset($session['isCaptcha'])) { if (!$captcha || !$this->captchaCheck($captcha)) { - $error = '验证码错误!'; - $this->writeLoginLogs($error); - return $this->error($error); + return $this->displayResponse('验证码错误!'); } } // 验证表单令牌 - if (!request()->checkToken('__token__', \request()->all())) { - $error = '表单令牌错误!'; - $this->writeLoginLogs($error); - return $this->error($error, '', ['token' => token()]); + if (!request()->checkToken('__token__', request()->all())) { + return $this->displayResponse('表单令牌错误!', ['token' => token()]); } else { $result = Admin::checkLogin($user, $pwd); if (empty($result)) { - request()->adminData['time'] = time(); - request()->adminData['isCaptcha'] = true; - request()->adminData['count'] = isset(request()->adminData['count']) ? request()->adminData['count'] + 1 : 1; - request()->session()->set(AdminSession, request()->adminData); - $error = '用户名或密码错误!'; - $this->writeLoginLogs($error); - Event::emit('adminLoginError', \request()->all()); - return $this->error($error, '', ['token' => token()]); + $session['time'] = time(); + $session['isCaptcha'] = true; + $session['count'] = isset($session['count']) ? $session['count'] + 1 : 1; + request()->session()->set(AdminSession, $session); + // 执行登录失败事件 + Event::emit('adminLoginError', request()->all()); + return $this->displayResponse('用户名或密码错误!', ['token' => token()]); } if ($result['status'] !== 1) { - $error = '账号已被禁用!'; - $this->writeLoginLogs($error); - return $this->error($error); + return $this->displayResponse('账号已被禁用!'); } $result->login_ip = request()->getRealIp(); @@ -97,30 +85,41 @@ class Login extends AdminController try { $result->save(); - $session = array_merge(request()->adminData, $result->toArray()); + $session = array_merge($session, $result->toArray()); request()->session()->set(AdminSession, $session); } catch (\Throwable $th) { return $this->error($th->getMessage()); } - $success = '登录成功!'; - $this->writeLoginLogs($success, true); Event::emit('adminLoginSuccess', $result->toArray()); - return $this->success($success, $this->JumpUrl); + return $this->displayResponse('登录成功!', [] , $this->JumpUrl); } } return view('login/index', [ - 'captcha' => request()->adminData['isCaptcha'] ?? false, + 'captcha' => $session['isCaptcha'] ?? false, ]); } + /** + * 退出登录 + * @param string $msg + * @param array $data + * @param string $url + * @return Response + */ + private function displayResponse(string $msg = 'error', array $data = [], string $url = ''): Response + { + $this->adminLoginLog($msg, $url ? 1 : 0); + return empty($url) ? $this->error($msg, $url, $data) : $this->success($msg, $url); + } + /** * 写入登录日志 * @param string $error * @param int $status */ - private function writeLoginLogs(string $error, int $status = 0) + private function adminLoginLog(string $error, int $status = 0) { $name = \request()->input('name'); $userAgent = \request()->header('user-agent'); @@ -131,7 +130,7 @@ class Login extends AdminController $user_os = '未知'; } - $user_browser = preg_replace('/[^(]+\((.*?)[^)]+\) .*?/','$1',$userAgent); + $user_browser = preg_replace('/[^(]+\((.*?)[^)]+\) .*?/', '$1', $userAgent); $data = [ 'user_ip' => request()->getRealIp(), diff --git a/app/admin/controller/system/Admin.php b/app/admin/controller/system/Admin.php index 6e873ba..5d87c0b 100644 --- a/app/admin/controller/system/Admin.php +++ b/app/admin/controller/system/Admin.php @@ -325,7 +325,7 @@ class Admin extends AdminController $page = input('page', 1); $limit = input('limit', 3); // 计算最大页码 - $data = AdminNotice::with(['admin'])->where(['type' => $type, 'admin_id' => \request()->admin_id]) + $data = AdminNotice::with(['admin'])->where(['type' => $type, 'admin_id' => get_admin_id()]) ->order('id', 'desc')->paginate(['list_rows' => $limit, 'page' => $page])->toArray(); return $this->success('获取成功', '', $data); } @@ -333,7 +333,7 @@ class Admin extends AdminController foreach ($array as $item) { $where = [ ['type', '=', $item], - ['admin_id', '=', request()->admin_id] + ['admin_id', '=', get_admin_id()] ]; $count[$item] = AdminNotice::where($where)->where('status', 0)->count(); $list[$item] = AdminNotice::with(['admin'])->withoutField('content')->where($where)->limit(3)->order('id desc')->select()->toArray(); @@ -358,7 +358,7 @@ class Admin extends AdminController $type = input('type', 'notice'); if (!empty($id)) { - $detail = AdminNotice::with(['admin'])->where(['id' => $id, 'admin_id' => \request()->admin_id])->find(); + $detail = AdminNotice::with(['admin'])->where(['id' => $id, 'admin_id' => get_admin_id()])->find(); if (empty($detail)) { return $this->error('404 Not Found'); } @@ -383,7 +383,7 @@ class Admin extends AdminController { if (\request()->post()) { $post = request()->post(); - $post['send_id'] = request()->admin_id; + $post['send_id'] = get_admin_id(); $post['type'] = 'message'; $post['send_ip'] = request()->getRealIp(); $post['create_time'] = time(); @@ -404,7 +404,7 @@ class Admin extends AdminController if (empty($id)) { throw new Exception('参数错误'); } - AdminNotice::where(['id' => $id, 'admin_id' => request()->admin_id])->update(['status' => $status]); + AdminNotice::where(['id' => $id, 'admin_id' => get_admin_id()])->update(['status' => $status]); } catch (Exception $e) { return $this->error('更新失败'); } @@ -424,7 +424,7 @@ class Admin extends AdminController $where = [ ['type', '=', $type], ['status', '=', 1], - ['admin_id', '=', request()->admin_id] + ['admin_id', '=', get_admin_id()] ]; try { AdminNotice::where($where)->delete(); @@ -446,7 +446,7 @@ class Admin extends AdminController $type = input('type', 'notice'); $where = [ ['type', '=', $type], - ['admin_id', '=', request()->admin_id] + ['admin_id', '=', get_admin_id()] ]; try { AdminNotice::where($where)->update(['status' => 1]); @@ -468,10 +468,9 @@ class Admin extends AdminController */ public function center(Request $request): \support\Response { - if (request()->isPost()) { $post = request()->post(); - $post['id'] = $request->admin_id; + $post['id'] = get_admin_id(); if ($this->model->update($post)) { return $this->success(); } @@ -480,7 +479,7 @@ class Admin extends AdminController } $title = []; - $data = $this->model->find($request->admin_id); + $data = $this->model->find(get_admin_id()); if (!empty($data['group_id'])) { $group = AdminGroupModel::field('title') ->whereIn('id', $data['group_id']) @@ -505,7 +504,7 @@ class Admin extends AdminController { if (request()->isAjax()) { $post = request()->post(); - $id = $request->admin_id; + $id = get_admin_id(); try { //code... switch ($post['field']) { @@ -571,7 +570,7 @@ class Admin extends AdminController } // 查找数据 - $where[] = ['id', '=', request()->admin_id]; + $where[] = ['id', '=', get_admin_id()]; $where[] = ['pwd', '=', encryptPwd($pwd)]; $result = $this->model->where($where)->find(); diff --git a/app/admin/controller/system/Plugin.php b/app/admin/controller/system/Plugin.php index 49a5157..5322be6 100644 --- a/app/admin/controller/system/Plugin.php +++ b/app/admin/controller/system/Plugin.php @@ -1,5 +1,6 @@ error('插件名称只能是字母和数字'); } diff --git a/app/admin/library/Auth.php b/app/admin/library/Auth.php index 92b07cb..f81ce42 100644 --- a/app/admin/library/Auth.php +++ b/app/admin/library/Auth.php @@ -103,9 +103,9 @@ class Auth * @param string $mode 执行check的模式 * @param string $relation 如果为 'or' 表示满足任一条规则即通过验证;如果为 'and'则表示需满足所有规则才能通过验证 * @return bool 通过验证返回true;失败返回false - * @throws \think\db\exception\DataNotFoundException - * @throws \think\db\exception\DbException - * @throws \think\db\exception\ModelNotFoundException + * @throws DataNotFoundException + * @throws DbException + * @throws ModelNotFoundException */ public function check($name, int $admin_id = 0, int $type = 1, string $mode = 'url', string $relation = 'or'): bool { @@ -203,9 +203,9 @@ class Auth * 获取权限菜单 * @access public * @return mixed - * @throws \think\db\exception\DataNotFoundException - * @throws \think\db\exception\DbException - * @throws \think\db\exception\ModelNotFoundException + * @throws DataNotFoundException + * @throws DbException + * @throws ModelNotFoundException */ public function getRulesMenu() { @@ -231,9 +231,9 @@ class Auth * @param $admin_id * @param array $nodes * @return array - * @throws \think\db\exception\DataNotFoundException - * @throws \think\db\exception\DbException - * @throws \think\db\exception\ModelNotFoundException + * @throws DataNotFoundException + * @throws DbException + * @throws ModelNotFoundException */ public function getAuthList($admin_id, array $nodes = []): array { @@ -342,11 +342,11 @@ class Auth /** * 超级管理员 - * @access public - * @return bool - * @throws \think\db\exception\DataNotFoundException - * @throws \think\db\exception\DbException - * @throws \think\db\exception\ModelNotFoundException + * @access public + * @return bool + * @throws DataNotFoundException + * @throws DbException + * @throws ModelNotFoundException */ public function superAdmin(): bool { @@ -363,9 +363,9 @@ class Auth * 管理组分级鉴权 * @param array $groupIDs * @return bool - * @throws \think\db\exception\DataNotFoundException - * @throws \think\db\exception\DbException - * @throws \think\db\exception\ModelNotFoundException + * @throws DataNotFoundException + * @throws DbException + * @throws ModelNotFoundException */ public function checkRulesForGroup(array $groupIDs = []): bool { @@ -395,23 +395,22 @@ class Auth * 获取用户信息 * @param $admin_id * @return array - * @throws \think\db\exception\DataNotFoundException - * @throws \think\db\exception\DbException - * @throws \think\db\exception\ModelNotFoundException + * @throws DataNotFoundException + * @throws DbException + * @throws ModelNotFoundException */ - public function getAdminData($admin_id): array + public function getAdminInfo($admin_id): array { - - $admin_id = $admin_id ?? session('AdminLogin.id'); - static $AdminData = []; + $admin_id = $admin_id ?? get_admin_id(); + static $AdminArray = []; $user = Db::name('admin'); // 获取用户表主键 $_pk = is_string($user->getPk()) ? $user->getPk() : 'id'; - if (!isset($AdminData[$admin_id])) { - $AdminData[$admin_id] = $user->where($_pk, $admin_id)->find(); + if (!isset($AdminArray[$admin_id])) { + $AdminArray[$admin_id] = $user->where($_pk, $admin_id)->find(); } - return $AdminData[$admin_id]; + return $AdminArray[$admin_id]; } /** diff --git a/app/admin/middleware/system/AdminPermissions.php b/app/admin/middleware/system/AdminPermissions.php index a38b1fb..3e89e31 100644 --- a/app/admin/middleware/system/AdminPermissions.php +++ b/app/admin/middleware/system/AdminPermissions.php @@ -41,24 +41,28 @@ class AdminPermissions implements MiddlewareInterface * @throws DataNotFoundException * @throws DbException * @throws InvalidArgumentException - * @throws ModelNotFoundException + * @throws ModelNotFoundException|\ReflectionException */ public function process(Request $request, callable $handler): Response { - $app = request()->getApp(); + $app = request()->getApp(); $controller = request()->getController(); - $action = request()->getAction(); + $action = request()->getAction(); $AdminLogin = request()->session()->get(AdminSession); if (!isset($AdminLogin['id']) && strtolower($controller) !== 'login') { return redirect(url('/login/index')); } - // 判断是否需要鉴权 - $request->admin_id = $AdminLogin['id'] ?? 0; - $request->adminData = $AdminLogin ?? []; - $method = '/' . $controller. '/' .$action; - if (!in_array($method, $this->noNeedAuth) && !in_array('*', $this->noNeedAuth)) { - if (!Auth::instance()->SuperAdmin() && !Auth::instance()->check($method, $request->admin_id)) { + // 获取权限列表 + $class = new \ReflectionClass($request->controller); + $properties = $class->getDefaultProperties(); + $this->noNeedAuth = $properties['noNeedAuth'] ?? $this->noNeedAuth; + + // 控制器鉴权 + $method = '/' . $controller . '/' . $action; + if (!in_array('*', $this->noNeedAuth) + && !in_array(strtolower($method), array_map('strtolower', $this->noNeedAuth))) { + if (!Auth::instance()->SuperAdmin() && !Auth::instance()->check($method, get_admin_id())) { if (request()->isAjax()) { return json(['code' => 101, 'msg' => '没有权限']); } else { @@ -67,9 +71,14 @@ class AdminPermissions implements MiddlewareInterface } } - // 控制器中间件分发 - $id = input('id'); + /** + * Admin应用 + * 控制器权限分发 + */ if (\request()->isPost()) { + + $id = input('id'); + if ($controller == 'system/Admin') { if ($data = AdminModel::getById($id)) { $group_id = input('group_id'); @@ -79,7 +88,9 @@ class AdminPermissions implements MiddlewareInterface return json(ResultCode::AUTH_ERROR); } } - } else if ($controller == 'system/AdminGroup') { + } + + if ($controller == 'system/AdminGroup') { if (!empty($id) && $id >= 1) { if (!Auth::instance()->checkRulesForGroup((array)$id)) { return json(ResultCode::AUTH_ERROR); @@ -88,11 +99,12 @@ class AdminPermissions implements MiddlewareInterface } } + // 分配当前管理员信息 View::assign('app', $app); View::assign('controller', $controller); View::assign('action', $action); View::assign('AdminLogin', $AdminLogin); - $this->writeAdminRequestLogs(); + self::writeAdminRequestLogs(); return $handler($request); } @@ -103,7 +115,7 @@ class AdminPermissions implements MiddlewareInterface * @throws DbException * @throws ModelNotFoundException */ - public function writeAdminRequestLogs() + public static function writeAdminRequestLogs() { if (saenv('system_logs')) { diff --git a/app/admin/view/system/admin/center.html b/app/admin/view/system/admin/center.html index 5a18dcd..825afd5 100644 --- a/app/admin/view/system/admin/center.html +++ b/app/admin/view/system/admin/center.html @@ -160,7 +160,7 @@

{:__('标签')}

-
+
{$vo}
diff --git a/app/api/controller/Ajax.php b/app/api/controller/Ajax.php index e3228ab..c5fbc4e 100644 --- a/app/api/controller/Ajax.php +++ b/app/api/controller/Ajax.php @@ -27,7 +27,6 @@ class Ajax extends ApiController * @return Response|void * @throws DataNotFoundException * @throws DbException - * @throws ModelNotFoundException */ public function smsSend() { @@ -46,10 +45,10 @@ class Ajax extends ApiController return $this->error(__('发送频繁')); } - $userData = User::getByMobile($mobile); - if (in_array($event, ['register', 'changer']) && $userData) { + $user = User::getByMobile($mobile); + if (in_array($event, ['register', 'changer']) && $user) { return $this->error('当前手机号已被占用'); - } else if ($event == 'forgot' && !$userData) { + } else if ($event == 'forgot' && !$user) { return $this->error('当前手机号未注册'); } @@ -89,10 +88,10 @@ class Ajax extends ApiController return $this->error(__('发送频繁')); } - $userData = User::getByEmail($email); - if (in_array($event, ['register', 'changer']) && $userData) { + $user = User::getByEmail($email); + if (in_array($event, ['register', 'changer']) && $user) { return $this->error('当前邮箱已被注册'); - } else if ($event == 'forgot' && !$userData) { + } else if ($event == 'forgot' && !$user) { return $this->error('当前邮箱不存在'); } diff --git a/app/api/middleware/system/ApiPermissions.php b/app/api/middleware/system/ApiPermissions.php index 46dba80..3dd4518 100644 --- a/app/api/middleware/system/ApiPermissions.php +++ b/app/api/middleware/system/ApiPermissions.php @@ -1,4 +1,4 @@ -getApp(); + $app = request()->getApp(); $controller = request()->getController(); - $action = request()->getAction(); - $method = $controller . '/' . $action; - $className = '\app' . $app . '\\controller\\' . $controller; - $className = str_replace('/', '\\', $className); - if (class_exists($className)) { - $refClass = new \ReflectionClass($className); - $property = $refClass->getDefaultProperties(); - $this->needLogin = $property['needLogin'] ?? false; - $this->noNeedAuth = $property['noNeedAuth'] ?? []; - } + $action = request()->getAction(); + $method = $controller . '/' . $action; + + $refClass = new \ReflectionClass($request->controller); + $property = $refClass->getDefaultProperties(); + $this->needLogin = $property['needLogin'] ?? $this->needLogin; + $this->noNeedAuth = $property['noNeedAuth'] ?? $this->noNeedAuth; $auth = Auth::instance(); if ($auth->isLogin()) { - $request->user_id = $auth->userData['id']; - $request->userData = $auth->userData; + // 验证权限 if ($this->authWorkflow && Event::hasListener('apiAuth')) { - $result = Event::emit('apiAuth', ['method' => $method, 'user_id' => $request->user_id], true); + $result = Event::emit('apiAuth', ['method' => $method, 'user_id' => $auth->user_id], true); if (isset($result['code']) && $result['code'] != 200) { return json($result); } diff --git a/app/common/library/Auth.php b/app/common/library/Auth.php index 8dfa2bf..19e7e9c 100644 --- a/app/common/library/Auth.php +++ b/app/common/library/Auth.php @@ -13,8 +13,12 @@ declare(strict_types=1); namespace app\common\library; use app\common\model\system\UserLog; +use Psr\SimpleCache\InvalidArgumentException; use system\Random; use support\Response; +use think\db\exception\DataNotFoundException; +use think\db\exception\DbException; +use think\db\exception\ModelNotFoundException; use think\facade\Cache; use app\common\model\system\User as UserModel; use Webman\Event\Event; @@ -28,11 +32,16 @@ class Auth */ public string $token; + /** + * 用户ID + */ + public int $user_id = 0; + /** * 用户数据 * @var object|array */ - public mixed $userData; + public mixed $userInfo; /** * 保活时间 @@ -79,9 +88,11 @@ class Auth /** * 用户注册 * @param array $post - * @return bool - * @throws \Psr\SimpleCache\InvalidArgumentException - * @throws \think\db\exception\DbException + * @return false|Response + * @throws DataNotFoundException + * @throws DbException + * @throws InvalidArgumentException + * @throws ModelNotFoundException */ public function register(array $post) { @@ -90,10 +101,8 @@ class Auth return false; } - /** - * 禁止批量注册 - */ - $where[] = ['create_ip', '=', ip2long(request()->getRealIp())]; + // 禁止批量注册 + $where[] = ['create_ip', '=', request()->getRealIp()]; $where[] = ['create_time', '>', linux_extime(1)]; $totalMax = UserModel::where($where)->count(); @@ -128,24 +137,24 @@ class Auth $post['pwd'] = encryptPwd($post['pwd'], $post['salt']); } - $this->userData = UserModel::create($post); - - return $this->responseToken($this->userData); - + $user = UserModel::create($post); } catch (\Throwable $th) { $this->setError($th->getMessage()); return false; } + + return $this->responseToken($user); } /** * 用户检测登录 * @param string $nickname * @param string $pwd - * @return mixed - * @throws \think\db\exception\DataNotFoundException - * @throws \think\db\exception\DbException - * @throws \think\db\exception\ModelNotFoundException + * @return false|Response + * @throws DataNotFoundException + * @throws DbException + * @throws InvalidArgumentException + * @throws ModelNotFoundException */ public function login(string $nickname = '', string $pwd = '') { @@ -155,36 +164,37 @@ class Auth } else { $where[] = ['mobile', '=', htmlspecialchars(trim($nickname))]; } - $this->userData = UserModel::where($where)->find(); - if (!empty($this->userData)) { + $user = UserModel::where($where)->find(); - $uPwd = encryptPwd($pwd, $this->userData['salt']); - if ($this->userData['pwd'] !== $uPwd) { + if (!empty($user)) { + + $uPwd = encryptPwd($pwd, $user['salt']); + if ($user['pwd'] !== $uPwd) { $this->setError('用户名或密码错误'); - UserLog::write($this->getError(), $this->userData->nickname, $this->userData->id); + UserLog::write($this->getError(), $user['nickname'], $user['id']); return false; } - if (!$this->userData['status']) { + if (!$user['status']) { $this->setError('用户异常或未审核,请联系管理员'); - UserLog::write($this->getError(), $this->userData->nickname, $this->userData->id); + UserLog::write($this->getError(), $user['nickname'], $user['id']); return false; } // 更新登录数据 - $userUpdate = [ - 'id' => $this->userData['id'], + $update = [ + 'id' => $user['id'], 'login_time' => time(), 'login_ip' => request()->getRealIp(), - 'login_count' => $this->userData['login_count'] + 1, + 'login_count' => $user['login_count'] + 1, ]; - if (UserModel::update($userUpdate)) { - Event::emit('userLoginSuccess', $this->userData); - UserLog::write('登录成功', $this->userData->nickname, $this->userData->id, 1); - return $this->responseToken($this->userData); + if (UserModel::update($update)) { + Event::emit('userLoginSuccess', $user); + UserLog::write('登录成功', $user['nickname'], $user['id'], 1); + return $this->responseToken($user); } } @@ -195,9 +205,9 @@ class Auth /** * 验证是否登录 * @return bool - * @throws \think\db\exception\DataNotFoundException - * @throws \think\db\exception\DbException - * @throws \think\db\exception\ModelNotFoundException + * @throws DataNotFoundException + * @throws DbException + * @throws ModelNotFoundException|InvalidArgumentException */ public function isLogin(): bool { @@ -205,46 +215,67 @@ class Auth if (!$token) { return false; } - $uid = $this->checkToken($token); - if (!empty($uid)) { - $this->token = $token; - $this->userData = UserModel::with('group')->find($uid); - return true; + // 验证token + $user = $this->checkToken($token); + if (isset($user['id'])) { + $this->userInfo = UserModel::with('group')->find($user['id']); + if (!empty($this->userInfo)) { + $this->token = $token; + $this->user_id = $user['id']; + $this->refreshUserInfo($token, $this->userInfo); + return true; + } } return false; } /** - * 退出登录 - * @return void - * @throws \Psr\SimpleCache\InvalidArgumentException + * 获取用户信息 */ - public function logout() + public function getUserInfo() { - Cache::delete($this->token); + $token = $this->getToken(); + if (!$token) { + return false; + } + + // 获取用户信息 + return $this->checkToken($token); } /** * * 返回前端令牌 - * @param mixed $userData + * @param $user * @param bool $token - * @return mixed - * @throws \Psr\SimpleCache\InvalidArgumentException + * @return Response + * @throws InvalidArgumentException */ - public function responseToken($userData, bool $token = false) + public function responseToken($user, bool $token = false): Response { - $this->token = $token ? $this->getToken() : $this->buildToken($userData['id']); + $this->token = $token ? $this->getToken() : $this->buildToken($user['id']); $response = response(); - $response->cookie('uid', $userData['id'],$this->keepTime, '/'); - $response->cookie('token', $this->token,$this->keepTime, '/'); - $response->cookie('nickname', $userData['nickname'],$this->keepTime, '/'); - Cache::set($this->token, $userData['id'], $this->keepTime); - Event::emit("userLoginSuccess", $userData); + $response->cookie('uid', $user['id'], $this->keepTime, '/'); + $response->cookie('token', $this->token, $this->keepTime, '/'); + $response->cookie('nickname', $user['nickname'], $this->keepTime, '/'); + $this->refreshUserInfo($this->token, $user); + // 执行登录成功事件 + Event::emit("userLoginSuccess", $user); return $response; + } + /** + * 刷新用户信息 + * @param $token + * @param $user + * @return void + * @throws InvalidArgumentException + */ + private function refreshUserInfo($token, $user): void + { + Cache::set($token, $user, $this->keepTime); } /** @@ -260,7 +291,6 @@ class Auth /** * 获取token - * @return array|string|null */ public function getToken($token = 'token') { @@ -269,15 +299,20 @@ class Auth /** * 校验token - * @access protected - * @param $token - * @return void - * @throws \Psr\SimpleCache\InvalidArgumentException */ public function checkToken($token) { - $user_id = Cache::get($token); - return $user_id ?? false; + return Cache::get($token); + } + + /** + * 退出登录 + * @return void + * @throws InvalidArgumentException + */ + public function logout() + { + Cache::delete($this->token); } /** diff --git a/app/common/library/ParseData.php b/app/common/library/ParseData.php index 57bf387..c59d113 100644 --- a/app/common/library/ParseData.php +++ b/app/common/library/ParseData.php @@ -118,7 +118,7 @@ class ParseData } /** - * 自动补全图片 + * cdn前缀 * @access public * @param string $image * @param $data diff --git a/app/functions.php b/app/functions.php index e776012..e9dd18a 100644 --- a/app/functions.php +++ b/app/functions.php @@ -3,6 +3,7 @@ * 全局公共函数库 */ +use app\common\library\Auth; use app\common\model\system\UserThird; use think\facade\Cache; use app\common\model\system\Config; @@ -123,6 +124,51 @@ if (!function_exists('token_field')) { } } +if (!function_exists('get_user_id')) { + /** + * 获取会员ID + */ + function get_user_id() + { + return get_user_info('id'); + } +} + +if (!function_exists('get_user_info')) { + /** + * 获取会员信息 + */ + function get_user_info($field = '') + { + $data = Auth::instance()->getUserInfo(); + if ($field && isset($data[$field])) { + return $data[$field]; + } + + return $data; + } +} + +if (!function_exists('get_admin_id')) { + /** + * 获取管理员ID + */ + function get_admin_id(string $name = 'AdminLogin') + { + return get_admin_info($name . '.id'); + } +} + +if (!function_exists('get_admin_info')) { + /** + * 获取管理员信息 + */ + function get_admin_info(string $name = 'AdminLogin') + { + return session($name); + } +} + // +---------------------------------------------------------------------- // | 文件操作函数开始 // +---------------------------------------------------------------------- diff --git a/app/index/controller/Ajax.php b/app/index/controller/Ajax.php index 9d7bcd7..2b86217 100644 --- a/app/index/controller/Ajax.php +++ b/app/index/controller/Ajax.php @@ -62,10 +62,10 @@ class Ajax extends HomeController return $this->error(__('发送频繁')); } - $userData = User::getByMobile($mobile); - if (in_array($event, ['register', 'changer']) && $userData) { + $user = User::getByMobile($mobile); + if (in_array($event, ['register', 'changer']) && $user) { return $this->error('当前手机号已被占用'); - } else if ($event == 'forgot' && !$userData) { + } else if ($event == 'forgot' && !$user) { return $this->error('当前手机号未注册'); } @@ -103,10 +103,10 @@ class Ajax extends HomeController return $this->error(__('发送频繁')); } - $userData = User::getByEmail($email); - if (in_array($event, ['register', 'changer']) && $userData) { + $user = User::getByEmail($email); + if (in_array($event, ['register', 'changer']) && $user) { return $this->error('当前邮箱已被注册'); - } else if ($event == 'forgot' && !$userData) { + } else if ($event == 'forgot' && !$user) { return $this->error('当前邮箱不存在'); } diff --git a/app/index/controller/Third.php b/app/index/controller/Third.php index 6a638c5..60563ad 100644 --- a/app/index/controller/Third.php +++ b/app/index/controller/Third.php @@ -99,40 +99,37 @@ class Third extends HomeController } catch (\Exception $e) { return $this->error($e->getMessage()); } - $userData = $this->oauth->getUserInfo(); - if (!empty($userData) && !$this->auth->isLogin()) { - return $this->register($userData, $this->type); + $user = $this->oauth->getUserInfo(); + if (!empty($user) && !$this->auth->isLogin()) { + return $this->register($user, $this->type); } else if ($this->auth->isLogin()) { // 绑定用户 - return $this->doBind($userData, $this->type); + return $this->doBind($user, $this->type); } } /** * 用户注册操作 - * @param array $userDatas + * @param array $info * @param string|null $type * @return Response * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException */ - protected function register(array $userDatas = [], string $type = null) + protected function register(array $info = [], string $type = null) { - $openid = $userDatas['openid'] ?? $userDatas['id']; - $nickname = $userDatas['userData']['name'] ?? $userDatas['userData']['nickname']; - $userData = UserThird::alias('th') - ->view('user', '*', 'user.id=th.user_id') - ->where(['openid' => $openid, 'type' => $type]) - ->find(); + $openid = $info['openid'] ?? $info['id']; + $nickname = $info['userData']['name'] ?? $info['userData']['nickname']; + $userInfo = UserThird::alias('th')->view('user', '*', 'user.id=th.user_id')->where(['openid' => $openid, 'type' => $type])->find(); - if (!empty($userData)) { - $array['id'] = $userData['id']; + if (!empty($userInfo)) { + $array['id'] = $userInfo['id']; $array['login_time'] = time(); $array['login_ip'] = request()->getRealIp(); - $array['login_count'] = $userData['login_count'] + 1; + $array['login_count'] = $userInfo['login_count'] + 1; if (User::update($array)) { - $response = $this->auth->responseToken($userData); + $response = $this->auth->responseToken($userInfo); $response->withBody(json_encode(ResultCode::LOGINSUCCESS))->redirect(request()->cookie('redirectUrl', '/')); } @@ -140,7 +137,7 @@ class Third extends HomeController // 注册本地用户 $data['nickname'] = $nickname; - $data['avatar'] = $userDatas['userData']['avatar']; + $data['avatar'] = $info['userData']['avatar']; if (User::getByNickname($nickname)) { $data['nickname'] .= Random::alpha(3); } @@ -155,11 +152,11 @@ class Third extends HomeController 'user_id' => $result['id'], 'openid' => $openid, 'nickname' => $nickname, - 'access_token' => $userDatas['access_token'], - 'refresh_token' => $userDatas['refresh_token'], - 'expires_in' => $userDatas['expires_in'], + 'access_token' => $info['access_token'], + 'refresh_token' => $info['refresh_token'], + 'expires_in' => $info['expires_in'], 'login_time' => time(), - 'expiretime' => time() + $userDatas['expires_in'], + 'expiretime' => time() + $info['expires_in'], ]; } @@ -207,7 +204,7 @@ class Third extends HomeController } if ($this->auth->isLogin()) { - $result = $this->auth->userData; + $result = $this->auth->userInfo; if (!empty($result)) { if (empty($result['email']) || empty($result['pwd'])) { @@ -227,18 +224,18 @@ class Third extends HomeController /** * 用户绑定操作实例 - * @param array $userDatas + * @param array $info * @param string|null $type * @return Response|null * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException */ - protected function doBind(array $userDatas = [], string $type = null) + protected function doBind(array $info = [], string $type = null) { - $openid = $userDatas['openid'] ?? $userDatas['id']; - $nickname = $userDatas['userData']['name'] ?? $userDatas['userData']['nickname']; + $openid = $info['openid'] ?? $info['id']; + $nickname = $info['userData']['name'] ?? $info['userData']['nickname']; // 查询是否被注册 $where['openid'] = $openid; @@ -251,11 +248,11 @@ class Third extends HomeController 'user_id' => request()->cookie('uid'), 'openid' => $openid, 'nickname' => $nickname, - 'access_token' => $userDatas['access_token'], - 'refresh_token' => $userDatas['refresh_token'], - 'expires_in' => $userDatas['expires_in'], + 'access_token' => $info['access_token'], + 'refresh_token' => $info['refresh_token'], + 'expires_in' => $info['expires_in'], 'login_time' => time(), - 'expiretime' => time() + $userDatas['expires_in'], + 'expiretime' => time() + $info['expires_in'], ]; if (UserThird::create($third)) { @@ -283,6 +280,4 @@ class Third extends HomeController request()->cookie('redirectUrl', null,1); return $this->redirect($referer); } - - } diff --git a/app/index/controller/User.php b/app/index/controller/User.php index 3168bcf..7fa1668 100644 --- a/app/index/controller/User.php +++ b/app/index/controller/User.php @@ -62,7 +62,7 @@ class User extends HomeController public function index(): Response { // 未读短消息 - $unread = UserNotice::where('user_id', \request()->user_id)->where('status', 0)->count(); + $unread = UserNotice::where('user_id', get_user_id())->where('status', 0)->count(); return view('/user/index', [ 'unread' => $unread, ]); @@ -165,16 +165,15 @@ class User extends HomeController } $where = $email ? ['email' => $email] : ['mobile' => $mobile]; - $userData = $this->model->where($where)->find(); - if (!$userData) { + $user = $this->model->where($where)->find(); + if (!$user) { return $this->error('用户不存在'); } try { $salt = Random::alpha(); $pwd = encryptPwd($pwd, $salt); - $this->model->update(['id' => $userData['id'], 'pwd' => $pwd, 'salt' => $salt]); - + $this->model->update(['id' => $user['id'], 'pwd' => $pwd, 'salt' => $salt]); } catch (\Exception $e) { return $this->error('修改密码失败,请联系管理员'); } @@ -207,7 +206,7 @@ class User extends HomeController return $this->error('当前昵称已被占用,请更换!'); } - if ($this->model->update(['id' => $request->user_id, 'nickname' => $nickname])) { + if ($this->model->update(['id' => get_user_id(), 'nickname' => $nickname])) { return $this->success('修改昵称成功!', (string)url('/user/index')); } @@ -231,7 +230,7 @@ class User extends HomeController return view('/user/center', [ 'newsHtml' => $result ?? '服务器错误', 'userList' => $this->model->order('login_count', 'desc')->limit(12)->select()->toArray(), - 'invite_count' => $this->model->where('invite_id', $request->user_id)->count(), + 'invite_count' => $this->model->where('invite_id', get_user_id())->count(), ]); } @@ -254,7 +253,7 @@ class User extends HomeController $where[] = ['status', '=', $status]; } - $where[] = ['user_id', '=', \request()->user_id]; + $where[] = ['user_id', '=', get_user_id()]; $count = UserNotice::where($where)->count(); $page = ($count <= $limit) ? 1 : $page; $list = UserNotice::where($where)->order('id', 'desc')->limit((int)$limit)->page((int)$page)->select()->toArray(); @@ -279,7 +278,7 @@ class User extends HomeController return $this->error('消息不存在'); } - if ($info['user_id'] != \request()->user_id) { + if ($info['user_id'] != get_user_id()) { return $this->error('非法操作'); } @@ -293,7 +292,7 @@ class User extends HomeController } // 更新未读 - $unread = UserNotice::where(['user_id' => \request()->user_id, 'status' => 0])->count(); + $unread = UserNotice::where(['user_id' => get_user_id(), 'status' => 0])->count(); return view('/user/viewMessage', [ 'info' => $info, 'unread' => $unread, @@ -311,7 +310,7 @@ class User extends HomeController $ids = input('id'); $type = input('type', 'del'); $where[] = ['id', 'in', implode(',', $ids)]; - $where[] = ['user_id', '=', \request()->user_id]; + $where[] = ['user_id', '=', get_user_id()]; if ($type === 'del') { if (UserNotice::where($where)->delete()) { return $this->success('删除成功'); @@ -346,14 +345,14 @@ class User extends HomeController return $this->error($post); } - if ($nickname != \request()->userData['nickname'] + if ($nickname != get_user_info()['nickname'] &&$this->model->where('nickname', $nickname)->find()) { return $this->error('当前昵称已被占用,请更换!'); } unset($post['money']); unset($post['score']); - $user = $this->model->find(\request()->user_id); + $user = $this->model->find(get_user_id()); if ($user->save($post)) { return $this->success('更新资料成功'); } @@ -361,9 +360,7 @@ class User extends HomeController return $this->error(); } - return view('/user/profile',[ - 'user' => \request()->userData, - ]); + return view('/user/profile'); } /** @@ -372,14 +369,14 @@ class User extends HomeController */ public function certification(): Response { - + $userInfo = get_user_info(); if (request()->isPost()) { $name = input('name'); $mobile = input('mobile'); $idCard = input('idCard'); $captcha = input('captcha'); - if (!empty(\request()->userData['prove'])) { + if (!empty($userInfo['prove'])) { return $this->error('您已经实名认证过了!'); } @@ -405,7 +402,7 @@ class User extends HomeController } // 更新系统认证信息 - $this->model->where('id', \request()->user_id)->update([ + $this->model->where('id', get_user_id())->update([ 'prove' => 1, 'name' => $name, 'idCard' => $idCard, @@ -420,7 +417,7 @@ class User extends HomeController return $this->success('实名认证成功!'); } - return view('/user/certification',['prove' => \request()->userData['prove']]); + return view('/user/certification',['prove' => $userInfo['prove']]); } /** @@ -437,7 +434,7 @@ class User extends HomeController // 获取数据 $page = input('page', 1); $limit = input('limit', 1); - $where[] = ['login_id', '=', \request()->user_id]; + $where[] = ['login_id', '=', get_user_id()]; $count = UserLog::where($where)->count(); $page = ($count <= $limit) ? 1 : $page; $list = UserLog::where($where)->order('id', 'desc')->limit((int)$limit)->page((int)$page)->select()->toArray(); @@ -460,15 +457,16 @@ class User extends HomeController // 获取参数 $pwd = input('pwd'); $oldPwd = input('oldpwd'); - $yPwd = encryptPwd($oldPwd, $request->userData->salt); + $userInfo = get_user_info(); + $yPwd = encryptPwd($oldPwd, $userInfo['salt']); - if ($yPwd != $request->userData->pwd) { + if ($yPwd != $userInfo['pwd']) { return $this->error('原密码输入错误!'); } $salt = Random::alpha(); $pwd = encryptPwd($pwd, $salt); - $result = $this->model->update(['id' => $request->user_id, 'pwd' => $pwd, 'salt' => $salt]); + $result = $this->model->update(['id' => get_user_id(), 'pwd' => $pwd, 'salt' => $salt]); if (!empty($result)) { return $this->success('修改密码成功!'); } @@ -487,8 +485,8 @@ class User extends HomeController { if (request()->isPost()) { $data = array(); - $data['id'] = $request->user_id; - $data['app_id'] = 10000 + $request->user_id; + $data['id'] = get_user_id(); + $data['app_id'] = 10000 + get_user_id(); $data['app_secret'] = Random::alpha(22); if ($this->model->update($data)) { return $this->success(); @@ -527,7 +525,7 @@ class User extends HomeController if (!empty($email) && !empty($captcha)) { if ($Ems->check($email, $captcha, $event)) { - $this->model->update(['id' => $request->user_id, 'email' => $email]); + $this->model->update(['id' => get_user_id(), 'email' => $email]); return $this->success('修改邮箱成功!'); } @@ -578,7 +576,7 @@ class User extends HomeController if (!empty($mobile) && !empty($captcha)) { if ($Sms->check($mobile, $captcha, $event)) { - $this->model->update(['id' => $request->user_id, 'mobile' => (int)$mobile]); + $this->model->update(['id' => get_user_id(), 'mobile' => (int)$mobile]); return $this->success('修改手机号成功!'); } @@ -627,9 +625,10 @@ class User extends HomeController } try { - $request->userData->question = $question; - $request->userData->answer = $answer; - $request->userData->save(); + $userInfo = get_user_info(); + $userInfo->question = $question; + $userInfo->answer = $answer; + $userInfo->save(); } catch (\Throwable $th) { return $this->error(); } @@ -651,20 +650,21 @@ class User extends HomeController { $maxProgress = 5; $thisProgress = 1; + $userInfo = get_user_info(); - if ($request->userData->email) { + if ($userInfo->email) { $thisProgress++; } - if ($request->userData->mobile) { + if ($userInfo->mobile) { $thisProgress++; } - if ($request->userData->answer) { + if ($userInfo->answer) { $thisProgress++; } - if ($request->userData->wechat) { + if ($userInfo->wechat) { $thisProgress++; } @@ -691,8 +691,9 @@ class User extends HomeController if (!$response) { return $this->error(Upload::instance()->getError()); } - $request->userData->avatar = $response['url'] . '?' . Random::alpha(12); - if ($request->userData->save()) { + $userInfo = get_user_info(); + $userInfo->avatar = $response['url'] . '?' . Random::alpha(12); + if ($userInfo->save()) { return json($response); } } diff --git a/app/index/middleware/system/IndexPermissions.php b/app/index/middleware/system/IndexPermissions.php index cb09b55..de26d0b 100644 --- a/app/index/middleware/system/IndexPermissions.php +++ b/app/index/middleware/system/IndexPermissions.php @@ -38,50 +38,45 @@ class IndexPermissions implements MiddlewareInterface * 跳转URL地址 * @var string */ - public string $JumpUrl = '/user/index'; + public string $JumpUrl = '/index/user/index'; /** * 校验权限 * @param Request $request * @param callable $handler * @return Response + * @throws \ReflectionException */ public function process(Request $request, callable $handler): Response { - $app = request()->getApp(); + $app = request()->getApp(); $controller = request()->getController(); - $action = request()->getAction(); + $action = request()->getAction(); - // 控制器是否存在 - $className = '\app' . $app . '\\controller\\' . $controller; - $className = str_replace('/', '\\', $className); - - if (class_exists($className)) { - $refClass = new \ReflectionClass($className); - $property = $refClass->getDefaultProperties(); - $this->needLogin = $property['needLogin'] ?? false; - $this->noNeedAuth = $property['noNeedAuth'] ?? []; - $this->repeatLogin = $property['repeatLogin'] ?? ['login', 'register']; - $this->JumpUrl = $property['JumpUrl'] ?? '/user/index'; - } + $refClass = new \ReflectionClass($request->controller); + $property = $refClass->getDefaultProperties(); + $this->needLogin = $property['needLogin'] ?? false; + $this->noNeedAuth = $property['noNeedAuth'] ?? $this->noNeedAuth; + $this->repeatLogin = $property['repeatLogin'] ?? $this->repeatLogin; + $this->JumpUrl = $property['JumpUrl'] ?? $this->JumpUrl; // 是否验证登录器 $auth = Auth::instance(); if ($auth->isLogin()) { - $request->user_id = $auth->userData['id']; - $request->userData = $auth->userData; + // 禁止重复登录 if (in_array($action, $this->repeatLogin)) { return redirect($this->JumpUrl); } - View::assign('user', $auth->userData); + View::assign('user', $auth->userInfo); } else { + if ($this->needLogin && !in_array($action, $this->noNeedAuth)) { if (\request()->isAjax()) { return json(ResultCode::PLEASELOGININ); } else { - return redirect('/user/login'); + return redirect('/index/user/login'); } } } diff --git a/app/index/view/user/include.html b/app/index/view/user/include.html index 15c2ddf..9a3d422 100644 --- a/app/index/view/user/include.html +++ b/app/index/view/user/include.html @@ -5,12 +5,11 @@ + - - cgF#VPqI%!F66ZsVx!rp&F+wKtoU}4=us*)Xg)lMuv&7X&gqJBaq)1Nv9;ev83bF^AYAf9lmi7#X#YwlyO$yLHv2!w=?Wp7TwB`qLb$o z_pnsq5I^ihye$L9gNO%xF4d^H7A#Nnep>yCbko~_t-zKo8zz^8hkNJTSj` zu*M^S)`V1rjb+;>J7V56i4h(U%)qIEBz7@*(A z36w3hOME3~Z=-HX*{?TD+s}o*2F`(s$Yr%XD16eEhgdckMPpFAqO>qaq}iXQoNKwa zCm<%6a#KmqlnUNyEO-c{r61;T^IY0WwT@Nk$gNr>cX#_aBpb*_fpzXyAYQfV-6u9Z zOtn`kRIqptJyfU%Zx_0LjtJEPPv5n2Tmtk$7Q^QDDJ5|~f zh^~p(J4s#8QpFL`9}uoSYp_-lnhbr3?n^q;gw(Wdd)(1zI#_&YGqPXm*R&8 z*#-h@x7RZzUGZdo2MN3i>2ZWFp!``1!b2nxM}f8-ksN6u9PlL9L{|LEg2O=)J1}z- zF&{+Qii!?4v;blHrg_?I{>VFqfqGBG3!U}-{VrJ1K-*%EwY8A-@_U~|1RNfzu4T>S zXW8^~^$l(L2V9714Wc{@ethu>lC|XT>`oCt&Z-TZQZ~~NeU@Ca=Z!s6TFc6Ic&=`S z$4I+~jnzGsI^=x5iww_o2a%Cf6^-DojF&bg##}yM-@47~jBE3}u`s#wJ>6{z+#+uu z0+U}wzeGJoW4P1@tc3W4_>k{K#=ISeWU>q;vW*>&OU@7U%S=uksp@_afGhCF9sO7h zq~`|Oa?H1T2q;Fo0`)Hb1>UwkC0>NeQ0~rE&Kq|CMVTu> zFe_Z=Ko&AXcQpQebX6Rz%W=L>;agB@!J!{)jpsJ^0NMq*q2dS3Im@h{Fy9!J&oEUb z*Vsdadr{;`!hNa)zeJV4*s7iK`8H6=Wx`lXA6pvewY~)Dj8wk8bZI1ap|Jg#!-(!1 zBN_KM&Tv!5KtK|C4fw8zN6n7PBX5mk`%N3wbZ;SFG%AWWRC#$Yup}fq9o+=9(o4w4 zJ}j~>3qk+@m^~(Byv=Dt4IU6juEI_I$|rV#NuSnaDDD^aL_{;7EoWOb#U+FYKeE-T z)U3fQ(jOOH0?14g)D(h_EDK~cXiy<~Vsrjo2UTykuF}P_b^^4sJlv8EEH+8H#iUM2 zT|~#S1e`SMHQeExT6X=M2l)1rr?9gEsJ(Rh?=6Zj9ROg)rr?wiS8iuB&uu$KUs0;Q zbu^>9C$)wnm3NVyZmPXc*OgT#A9-a42{m#F;N|5|d$o2zS?d$5@iJ-3W|LbiqJHto ziyjQGFW}0dChgUIu`@mPFn)#rQN3{`IET!p>fOikHI<)^jmBFvg#F{km`EH$^;?#P!9!9aMda|u_yH^xc) z=s`)pg?pzwFpX8%Ip~+KUvKMWIWfK+mB3BDLpxaQjPlY@nfcl-{4iEmoI<=#fw5km zg(Wl_P0M+c^gIILyrB<6B1T>}~^i;wuGqYJ3?#2^pBXY1LcK)L|8!vy9E(onx zlw$s@Ftu46+ItI0B{BhQpS1s=ARRxLsNe2&u3JAhVzey8gx4W2CCIEVjx?UmyDP*n z-p9{NTfay}lK;+Wo$<{ZG+_nS|{^9Y<7qzO2nYIZ+*6h;iE>zbK~nC6!ELJumTN zqgCw?Q|~~Q6OQo14NbQP!=kDOM;~Vwzn0lgbygBQZ(2H{R~RjQ+)Bu(o@T2G$au+| z!(rX%09W1|)(`Ifx-j?5qABJxEEJ<9tK!Rx^Cmm-Exc2Q$|D~w@=g1Fp5 zf@y|;_rtSya{btCEr|n+kN}Sa)T0}USP5dtSLcoQUpJfXOam}~GeBxS+h5q%`^WZm z-LRD6)DRVo4E3~}xHy$~gvz9peDwUBlANL`kX*ed;gk}CP`wgSI1t& z+}_z73Q16H{||+1HF0_K(l}hDIT79lgi6XHHf8)WM7<0<;BK^&0^`Di{71mV>~!P2 ztW*>5f7_cRNY9r}`0T&_=ia2?=l}nZBI;oNlaBBol(_bc2%7M@RU1LGcuO(3J@T;G?q6D02A>8(J}wY+RV4eZ>9~ z2POYANm)0@ z?jkrIu51L0G?<`PmL&udp@CfwP~O}1uC>z{s0{V1rOO+yw&O@JRF*yyRvay?M^Dx7 z%4LjzR7CBX2qk+ko{o@I8!!;&L3jD(IYwf^R?^pL92!j#in-c}0X59vdAPI$7YU5!m~M!Kujyi63$A zDyYhX(sm&SDLus&KXQPsHj}%@z;dX~G5z-(`U97IwfqYboyW6(lhp4hdwTX$Lby*x z;a}i%{2lXu6oC9qQHYeE4(p{u@Z!C~PvX1wg7A1KUeJx5w;Jzdp+3JiBY%4cKZpFP zY_D4@WnyZIYjmqLntB(5!#JAi64c#Bn4s_}S)T|Kv#zh72NreoVI&C( z&B+%I<{O+@*$%JWudc}N|3)JxOGIG0_|#X(r@r|AGBW;&1(O)t^P9&IeA(OChrm*f zXK}f5ubfmfUJG0&Za(Ap%}@1OoXh<-QfRZ6WuCjZqK(}+*w?4i*Tfl+S7$mVByD;{ zfo|g=A=YF@La9u5rR$D!7MEdw^Jgv;>+1PFshDU~VnlkG+FoDxuulH!hBsn2jsTYR zgEiV$BcLTY^-pn*m(P2 zp{C~EPxW0bvlBnOPkjH4_~axw4B7Xo#OqHp`(G&2>XVcIcXu262X!#`GT;&cN{~BZ zUD!KO-g*E?9aocLWT`FPIh{a;K7`b3IP1M0;L_gCDG++KksGTqnvdL_H2R=kr$@kR z0)4SiVmUK*ykzz8$z_&Apf}|FR;zmvhW8pjRD2fjv@t#g$oWK7mp4t%*K% zgtIst>`>lA$Y%6m|RchRgkxctjWg$)?Z)$)jNZt|C~(z)KJ*}PD4)mPR@U0GbzgPfwGXN z8bb}UhyMK8D8U|@SdiM9>dv0(ULxVrt_7v$Cv9&bW8n}gpkrWDTA^SsZXj+>Pt%}x z_XJ9k`s5B}9Liq62P@$+uX25jbu#I}k>zUbxiIx;^*)1sie6&5uyQ1*gk+l(8wWI2 z-w>X4vE*;{9f**xo;ui0GqcgghuxdHkeUrh0v}01?bR?39>$6De_%=dg~O|VcS`@N z$sxS9DX>p1qI??eXGyvLbaHpm|3umEGPl3ITwM+A5ZTrzyJ|CY{?I1DBc=N*% zDLh=+(%Jc(z@gZrbb+E>YyQT-dv`XKlHix#9Y!gE0~{Z^(bvLVI}Ei*9$Hlf6=W0z zN(qGbBs@~QmAJ*PL3o%TDfnkAp@!hKF~VP~hEop62ZwL7v^_eE2X9Sn z*PjotT)=-Z%hh12{n3| zqypms5()5t5PL~{_*$^P@h~h7!qv)Umm9Z>5gwuKTf4HB9i*7EB^hJs@iVlibxQ9% z#Jjnr0^gCWZlppl-Fv{XohHR!I1 zJwmS$y@aQ8^v10cNaAvdOnq-IG9Tj4*^umNEoji9F;Y?CHy3xWGdmgwnMoNgRK~5n!WfNO zws_l_e6#&?TOj`bIy`?=wg3F_|6!D$w8H;kfdAEy!hKRW#qZXV=t@g%#2_lT8V7TZ-*O6tED>4)V3(O3}>@PJ6zo6Cgq zAGnp1=$^_7)jpilJ(8eYb|89~>Tr8I7+-a-t6T9zg6pe2$Uc`3mfH=neC3HCA zE8C#4G#)SdvPal}XohiL%;@m8k>XJ)2zrl$lZXCo2+5)!v484L_f9AO8;A`HmN6_= zwJLptsd|9ElVP>`!rt+ZKF7~1L}9uG{X6>zW6RO)u; z7g@s}&`+m+-FVUSzA$zXhudZ)E%Kt_%ZiuGQmYFgY6{7^Mt?U?d!w?azH*Ynlp8oA z4oo9yfL14%LObG16vi9D15_gyr(qD_-2us-1w}$;n^vyhRO#Z3{GnTJQn#wBn>1MG zi%GA%ok|in`Q^h@NQb5f9O9>ml(C??#@mlKo{!8VI|s*yd!-E7TLtr7MH@-?kTO@* zHd{+YOEFAbe^hu~{8EQ*qBYbA%N0~;U>X+sU?KyaGjRVVo1Ql~7@|!yn=L_uhQg?Y zdGc1Ia^4AX9BYp~xNMakc`{OSjgCzC!7>Z*PCfra!gySnIjY(oF&509UU=uDqUxjm z9+Jrr@f@0D=5ejDTuapSlluZALOh}eBM-M&bpn_4;DCY_@HvGFWU^IT{#imvps{j3 zNO8g_SKVVQ?2LJS?Xf82EV0ZS$fZudq$#NA*j0VLH&06suC8Dm`e?PrF+$RDbqn$h z0Ew*pG&shAEgr1#V^|M%@GZK2xXcXGlP6_Jj22dS|EG}jw--Z1gXqZo1gq;OSpV#5 z@Bap@|8Qg(38ONrd?>+-LL;0oNHcswKfZkzZfQttt!zCZ6))?_L@0~ejXEboEtxAu z4-iU2nTGxjx;}*_FNGe&RIsCeV!x)Wo3k5%8-k$bWVD>Od-K75$caq5HV37_gcMm^ z{M{Kg6oWu|l&FNPY?(p92ojTxa+^U#0*mxUr?kXS55?$C{=PG-yEjyg3JZ|Fnhq~| z6LMzvnuIpxyND3zF-LPz3S}*n-OujQ{6Idu$a0TPKWdxZ3XE(~&im*)UU2M6Wk=qQ z?7Q=KO4G}TOy-ZwbH3-h4#(S!Ml3rkdvhvacLOMymp05#S4V?z4{MW%2>2c0mKcKM zQRE8=a5z{}HwZl{3>yEyq#m?KLg~atnq^0Tj-EXvUjTkOLyxkE6^%A^?ueF0AqCp) zYM2V2>p0O5JUTMN+)+Tti ztKj8X7u)X;%%xf-F0wkUhnB~wmU~0k=rH^16_YY=2|c;vzz17i)jw=crCwju$G=!G z9~}hW+^3w2!m^ze9L}fT>wT%qqlBF6xpn_CF8y){`LN}>!c~w#9ZXL`9F(uCH`BuQf2Y~iRLD;&eBJgs-;H)#{cDK!pjrqWlt&y|9+ z2%+PyCKRle@+}lRRq0iPhSDf*+mMK`&%7(Cq=dht={4<_*mQ@i9)b^+%^OT$3O|tN zaHa2`&k0}u#H0UrVE@x;0r&s*l7F2Uz+@DBsXi~Am?Hf1+8{3G#;*S~D;WRnh7?+T zXE$>zbA1OIGbd}SarF<`RWY=WxBM6UgLaM}3GuOHQrBeCSQXE%wG!TQi-JU%vXPpv zTMISRtLFR=p+5RKpc2dHK%#A{y$TxZxR4#^fcuh9rVLRxiXTCqyP?RO_CP4L)JPM1 z<71v#?w3!Dwxs6jsEN7XWMYMVK%kLW*mz!EHVZxw!U;1)8;;!-G%9e$ z(-%?#e@+Hn|88lX5?RBQAL3X zjl7)0F54Z6>uZ|#rB;Q%P=<4v;fgrtp)L|P%si;(XP1l&O|yWkg<6Si>xEH8h|pJ$ zR8Pwk0EYCwl~4pF8%Cv;R9wBabGoKiD+(j-K{80PlGnb!IbL~&8Z;6X9Jb5S7jI0>*+9z02I=x(>Q#!39!gsoHv_V!V+srqWTDA8g0SsF3p3jHRn2qS4&U-y z@58ctNEiB4P}@Ecu$uJ`a$ZTp&xk+)8fN?$ zX?eevw$|vnvz{ino}1E^%9_e+@O=TBVBJ(&q)fv?Zve731lCc4E|;g(9VHL&2pnzw zMZgUu9n*e+XbjxG$ie#6_x)kAtHyV&@lf)ck4=%ai`46?Szb zltS<<;h=Ig6n34XlTyZVNyS^${jA3&Y+vw^dPT)KAK1eRoz=MeGMQ2Hi3TJ_|8o2I zjwBv+7q8cVamFtX>z29GlyIJ~ArAmN1`oZn0ads{i+I;9<;pYOGrnPTutSfUY}4lU z>=b~IoX6+UvWbhTiD_RMYgFb_{Ph_BmVq=j1{_xjGK~ukH2=VIdfXzR=ZGk?!bGHF z#!^c#rej78rkdE_Uw{!}dgDiSz(@n#(DT?nJgyT{t+j>%lz;pQVcK^)Q;SI2=Q*_i2ZMZ=c!6gE*(4RphYL=$nb? z=HEYN$+UDznatBZA(Sh~|Odb`4P^bDoQ9EKGf+I_A zcT~W4E?D_j^|WWHixWDQ7?jB*d1tv!PkH+McE5=iHAl{llWzsIVd;g5Fglr}nNmWA zUX&<~9X8<=sYSbe)?}@_LK!+yZ0n898A-I`$A)-bfM*wzp~eFzC|-^bU!iWW|5~U~ zJWMe3Ab9-2Zv;|&1kr`>=N@}DZKBs^fbXfVSQ7#=E@ z=Spx%6ZWXMQ+%CShHGaDuG>s|QXA(!u*BxMm=4fRiExAki6_&!9Zc3$ZvwXdGFL*g zU(6MmjMmEw>*xfcSsi-A78y+@VK)Zc5hnJp~|SxvM8}DnQVz^iL^+hPSS1yh5lM%AC5`I z&SO+oROZYWNCF}mb)_5eE6v2c3xiqY%K*AP+x3GmvEb~n3`S_Km%OuM;pGe)qX%;F z&r{LVXDZq5V7JX3FqQ3}r`QkOg5=6#IYXuw!MOZfvS=gofx-$JB(l27aBydy&}WII zXVx9OKkAxThQQh_Xem75f9zU&Uih59C>TfUba-Ip&1I3_Mqd|pt>t}2>3R*{%}TTV z#p+loB;|B(A6|%W6AOy5@1WQ$Qb0L|pMa3hPHyj}4HA2tvLQ%>^_sCWkKns`XPZ zC2SN>o$LqPiUt8Q6Y1*=np%buY;c(bn3A_=;XGVW#l0*K>ZT8$hL(+NIByoi1M<1z zhr_*)*f&z3x0h~7e#AykH-_kjA9ucWn?T3WPA_$G z*~}&_=rJV+Duw%%y1#k?9kWWv*(%NYY!(wuk}o+@xSp1mJ5OzE=t5tnmvTP-enR^9k=Ebge)STv0V@aqfGN(O-_-Ue z5s0C_gV7(+$GQ5kCAKKahtAOlowfwrk%eJMk^!z>QDrns19Eb6^KGA*D{&-)riL+3cGxReFB_tE)<=Y1RWyfZ( z4G1=cJrf90N_KI+n#>P9WpTn{LJIO4*3IwjB%^%t-TUyC7t=0JUgIY20e)1_slj!8 zBvXxfNdE5NqmVm#uwq(ZkD_|KeCk3{(DL}(mpc3Ncc>0fFJHFT<>gM z`c7she5|ywQP{_-QeC}`+&_5Lg(pPiqIqkzeCxPi z%;93fRPo_5)y{r9zQA4SJc~cDxSaRC(Z`k56%0N=n!3D_Dy~J=l#!hwzVR@5JERZP z{`waEp?b(S5L{ojqA(uwY-}T=<-?99gYeXivTMwf&?}=j}POMliSwnxhReq?X9oc2!ua|z8-wk)cUzbB1^aD28fvty>85dhG zHqCqd8DN1@^d)gzG?7=9vgmW~A+VeMMajqW;+*%FOEmVCj`l*yg=$GNxvT4CzE|yg z4T6Kg{@fWJR~wa;st=EoE52vjrWV@$T3tZfb$wcFTg!z_d-1`Y?Di^R020g^l2UXX zdOrQpE3G9Xrgu`=-euvnlf>JQ7v1@!FLyo#t1jbCBkILAegKMaXmLWYx>*^DrWMgz zwIQGVDOr}ybvpQmFbW3nB0}_+G1&=f??W{BvgAIt(9j6?-o|1q6lG}4?~wR?@-opT z^wKle+fVawHo;GGLHqftu8Y^Gw~N1yY290Z(?59#8rM z0~@|gr%Ldgzuruxt^}DR2GD|*@Td;2AKtTmX#Uy@EV}tgR$_sH#CQy&wl(d5d(gOM z^D*CXaQfVOz4<<_-OHr@Hm>b_Vd?hqT_7sb#>*$83bEt;Cnv!+M*H$3;6^iKr^hiX zG`SnLkY$R~06!DH4eARi7R5X2B7$zt_1$>&_j<(czc0o9%MsmMjT~Wv0|2O!{W*W) z&m57hwVkbvv5nIo-e_J!-Emd~$M+IuQ(vAFIitOn45N4&=Uxi=H-N%z2!90v@?ns}09wQ0dt}`lPq9veQ5U43Irw3Vx32q-7u`6V zp0x5p6p$S?VR>?^d;^N4K1dK$BZ=(|$4#M$5CH-lu%E;1FK>BkJwJqJD1dShUSxi* z0EnfK$*5BUK^iyApz#k^TdC`-FG9==k+dfHh0YcCyH|h+2mujW%SD2$6Gf;UDO?f6 zD4VIviw7N}!2O&@QSobA7|$0f0eGV;^AQOYn};=W(0sfg^ubIE8=Orc+RJQWP0koT z=N-R{+`n)9qA#)fgSn9MSOWG=JoTJV3Rh8bWLOzP&;l2%llG27GJXzEUz2!@^;Dca zAH`B6Y^n=|L`jzRa7TzQP#g+Orq@R)G(t&~fUcSfP%wx=;)udoQ;DM77iEE6QSxa} zbK-UMHDtecA?fW=(Z^$E!fHu2n7uose(|xn9$ykM*RqmiCwCwTW17$W^K#aJu; zxe+<%Vj&U})64UV&Q56DUJiYk!JnM5)d=d)>6X&jdQe}YpiHFZ_x(xL7W~TfqGVL{qJ!fkuA=bWmj6UiNQ#pvyA8#5~q$;cUUQxj&l!@%lGPlptL7sGf6 z*tx1_d~pSiKNZxP?5Yi0nm*0j#6Z{{j4|l-kz1iZg5xC>i>sVCakDy$9e_jZmrIJ} z&qbn~q3H_pEqc|?_$-(2f;X7g%eF0tPcCBX<20DLhHU?0{6RDBS0Ce188ORHc)S}qe=_G%rDo*wR75xXjS(L^ zl`m`aFk_x7(wBUGoZ6XV;Y5yyrdJy#1QRY_Ei-vgk4VGJ_gPZivRG5ZAEg0UraR>D zv(}=PfQp8fWr^7m^p9ds8O_RiiM2cj+!lVk7^^4k;to!QYm+@nFfVUm$n=iuN z6stxDnQA8$38y_e}|}|d4%l{Rc1fU-yw8*aTSrr!Dfk= zQ)4Zk2Tbn99Z$wjYMo9&Pe2Id?N2FJ=Oq7SvT}RNlsz$Zn@n6lXJum@KvcX2aQsQ2 z1pXbbWJx<|CjVkESpgImE*wpgLCF|%Z@~S{{o^EwTOM4v(!bR{%~0Xw)qL5)5SXfwsQQ69HPRRSnpURM-aP9Bg8GZwfUQpkMn9yrhST94;${A~L|j(n&Ki3XWi+d zi&4g@yGjV#zjWa)c0Co<%chw{2_>t4`q%i2yi`}B!$)@m@46pVPK-_s8m1Qc-Ya-z zE6LMaGX@`3YHL@=Zu{%oq*d{C{Qr!gFyIdIpfuXcBFPo+0M0uYj285IH8kd7kjV1W zX;<-afv-|p-#4T)dU_^=m#~F|_;fxj;QO7kDBU0%#qQio=)dy+f!bve3AXt zP;*}FV}_pQ{Y4=I{B7K{f~~>l8=?YLZ6z&Y=EPGiuv6H5{EWfvO#G z0Lwi?c7TlKMqdGW)-r|5|Gcgo80*eM021>zGv1kr$pK95@+uDj@%Ew7RKT$07P@}K#FF}?2Y=1lpU_1eJakg)H4jb)2d{v*}hR)cht>XBk) zFqj$Cc0yZB?X)inrbE*O84&OvDz5QwJhp?A<9~Pm+CRVb_ILMa z^3GRw#^B&#UqF{)NEVcNTRkYOby;Y-w#9Pm)U z+^Ktm+0)d`PrjcUo^{yU)=D+5sgZC{w4YZD(NeViveV;zbJa6@O?f#iDq(9DMrDW- z{RFd&mB>j8qY__1P-$zn3)Q>%VZb^G17n|gLf0vkTg}s9#l^(=uR){t8b3p@LqjY@ zP0(^br;(U!gIeTe(fx;$5*-m?%IjbsqSCA+xtyV79qu5zxu}%SFpE#da zruQ>KViCx?A*EtJglFJr0J7Ps!XO<+Xfj25yGgVy)nTB2%cs8<4iat=?^;B=GBc$R zQ&#KNX{Dwj!`&kYi5)^SdEZclbUBK?&;1AVPcfhTTvL6ZjA2k?Tn(oiE2? zyy9?12z0Odpo@;0sVMt~yXKnLzN>kQ1of$ZCw z9yXl|a8x4p+UE)#4c2F&BcqPn&Z{b$cSE+Tv>$Nf4xZ1OT*yTw-T28={_37pvzAp= zY;`MQji@rNR-bO^u#^Vc!Xh`Yno}F_W#Y=*6cUF)WhD~SQZ%F7GMjIH922-VPU5X7 zB;)wrAHv2r@NrKwA|^pS-q+KW*Kf&BZ#Qf{@~w6I+^(>3MK~OKUz6^x z*AbpanJZweE|Y+qE!jQt(4TYZ=2fxdZ|(^XUskzKi&O&STo0R}a9XRj)pWX_B(PZn zv$tB7cV9Y>nV|GXZ;iceL?5lRv^LZ{JL(+=yzg$SieL>Tc4u#n{z<#>P^`Ax>b)%> z>z-(#02SrxzW42c?L>A{U{9;6Y&KqvlXlc-_GS@K-iXT!?*ZkUE&t>kEygmR*i@dI zQEl9L&W}|?Y!{YH)bDr+RyK}#+J%A@ zT&^Tg-61CX^ak_!6B1%3=91{ok0|t+^?5Wb)LG37Yz*G9ui!?jeOu3ad`q9kLoeWF zgZtC5gPPKNf{G+w5;zN)Fb(fcu4Dn_hztOHx#Pn){O8JZgpN~9S;c?khOER04l8;M zI<0lJcGPu(-&|S;r;U(5-%LC~fv*p0S9jg(K0>%u6>-v7e79S)USSarkm_^>(}E5U zE-UWT;}&{I@rsjV79nC3FB5_wDOC*j>CnU*mxfz+!RV7O`xXq^t7_36u*Y z!B+d(uK3=&2_if`HxrdB41~4?VXyF-uk;2*(-XP3h$3^*_aL(n=C43*f2C)3>h`Dw2g+`{b0~JF7r01EV8rGK?DWudB4V42LWzz5y$A*+pUon zmvEp7O>q2vI}J^I1B`AbWq}aX`b=|udWg7zHCH=`4hHxvuv_}Ry>MJqw-*8|>JpWL z_)f1`CXV^?=GW>Ufc#OH-fjJ2ai2GnuGl)xp)t_-#t=EobPX(o0~NI|DL(HTqT9!7z*KlyCT zL)M>e^E?kqoKn=--nEHyl+Z_i{;k#h?HLdFu20ESd;$%R2BHUf%4gVoN?6J=b1ml6 zS8wq*N!6#gvQkH^N@u4n9&3{h6L7}doC9-k-`3*gXW^KM5DCRw*sHzH_kF!}6VMa- z!HBtt2o5*UH5`PrUe~g_Yj{C6D|UOr+*=TmT;yyr);rvpuds4*>wP#ey zWOiQ2(#JF^dZ;i?FAX2=u2`KgMtA4Ohqf!ApTD#k&IQ;gfFik{bX~w1EmqHHs zQ+j&CuJlLjVoNl6)N^^)5G@_dNM?|L?Nhc4F$qZQ;-A8whoG%Ty2O5&!rZR8Si4)6 zP)JLN(h`>;E7v~&PvRNRs=c0!?#t!Np=-ftS|LkB|^bN#D#3kfd0bk1!Q7kF*+Vm>N;cX6ysp0Q@BWm>>MZ zQoui%ubKqP{-;6HfYP}Ek;A~Ggi*AaVARJSOPN*F&a z+p5EH(vz$@-`n%5!vbV7XU~|}YK~2yOD*A3O|J`Du;sp>Rx{iv_^>-m(DthssXG>@ z4XiWN$sZ8rCQ~JtbY9zx7sk2^d)9(T(Rzqu4-h zuxHF69Ir$@+Y4npRa_}k++BErSdliqAv{U0D2nh1nW9qt5s~ogd84ok@hF+RR@nUv zGdlIZo+J}gO1c_e6ZpZ~&!WBf79>LfW8y}u}uj|^)axE_eVxX*!?{} ztZ5Gz?0xr!d~D!K-B|jAPQ>(HZ?WWmWebX9jG3@WVH)7n<>{pT`1LC$i-0nOmVM~~ z{De2IIh zXO|{@+R>pR_;Db{h{w_XitD4Kn+W%IDP0AS1x^HTQlgy~b^U}lGa)#N5&}WB3?bF4 z*_f_P?!v*%qm?_K@^gTj0Opgq_n8`{;n>d2y8xLV-kTat+tp^=8Y;IPEQ3;TS?N1) zlyE*S(7%ROKfAT?1|(pWrq7Xv3ZjecCbRcRHm=xztFyhHWw3@%I|!tU4&{87%L<# zzWcm*2$ztDnVUG^amU0R5AP#S+WNfP2Q`pWP@rymxfwe?Dai+%_5Qv>W_j8k6v}P4 zXGV2!a74SE1$4fL%?n5{`Fon{TOaXN0g2S7A$tC#IpWm>72Vrq)UI6n!eih=j!8TA z2Ug|*Q7I!woE60bP#-TE9Br_;zB!7Pk&9DY*;ereC}1YQN-0MC)q3fPwaAcqpvpHw zp*fCp*l3IG;G-KaCpx;`ZvKdreh|r%fxC#DY9b2!8eXc45m!kPR4_6*4_|xM4@1+C zF1x(-f?zcN=aMxbm=Z*jrT5eC`FDY@>=sXz_72IOT(2B}i;xjNaZP1eh|&iZhDskL z8nAZ`rX*}XfaLo46T{WV{GoC-|0shKrbh_?-$k7Rf2ZxM=Ht8FPjL$BtjIFfwrOU^ z#@nu>*^NstE|?bjdO@xFB~|O8RV#I-1p{&$-G}pFMn~i4){DPde7o$X_kgyhEw$H* zMpw^`IQX7eL~{0Y(pKLWm_A}qX1#9V51(Nn;;=lgcp>Z|lHcSwIp~=mu{Qh$nd96v zpoLTeVF8*(KH9J+V)$~wT$qBR_Nq*P8Ow}PTI>P~n1}M9rQ(#k2XdEGj&4}Up~Pi{ z*tmHarz>@xN+{;#f$&Y7RJagrum+~602A9XdF7S(*?YN)089ZJDY66T;dQ2DDWr5;1%nSpVv zRI<)ZiBPHSQL`+pBNAT{Hn**05Z410Gowb#97UUDLP=XHNrjkxhuJ&BM;#GJh(%7Q zAWhjt(~n(Mi9guGF{Z~fqcH3LWOmRZg=hQ&+YM^QMwMe>S`WdCx(j~32E;cxw9@oj znfbD?QjqOknN1D$e~Q2Mfd?n{b>?-~B#*4~ZpmS1NA2BsJ%k&-uL8JUnmr$Kud~}% zK7n1Y6PuGGE#ZDPc`G?!EY%(!^UT%5KEqL@p&{sk8gE)WoYHUO*uCb-nk8jxCyLc5 z&Q#uJD*9NRM*ZEtzWyKUbghqV1MFWh^3bo8?>{*R{?l;(OB}NOZ(UKdVz=xdJ^T*Q z5f1E4N~HQCZC%41P>r_&%~qR3R!S5!ITQ}OA_+WPpWg zG|1JrSOo?6^kS17^DL8yVKxQC(CPqymzNm0%~1`kvlEe7_aeZIazgAM!S;?F>WJyV z#gEAX&D|u|ScHeATrQaCsZC(Md{{c{)-i&8!&(qP64%;Ng`^1#r~$LxlQOZsjPzMp zPb2vHkX77G&BF1Y^VeV3XXYS9>&e1YLhWLHho;)3iUxOTYwAzYU>h!EZ*swSfz3;C z3>V4s^@ZrrNZUrP{_X#bw)kIn)PKS{EVmE<0NpwO04)DW1oppIa{V_6=)YuKSJ>vZ z2P03O{@-9B^VAb&_=NL(lq_Vu;QA=NXVQ1BdDbn!ZE zjJaAmFV^mDy2Ec?XU)XV7QcgGIf|+xImLCJCH?mI?T)8AkEgrRU)YE431p1{7zP~l z(jLPt;tYj)!1FNh<9qsSeVHC{MG$!L;*C)7O8Rm@Fco+C@Pp4Lth+D@=Ha zsFR}`OX{P*#s!6c(_<7D(@!cz;KRaq^Za|&W3Y1sfDHni1$m5=YGW?cp}pWb5#p_E z07uJ>mWD_OK&CN(s33sgEUE+IlTN{DONuUo8>Yn9tl=0*h6SpTCTq-r?h7tWuYQ}V z{Rv3y+*m;-94vDMQf1_wjI0p+!$I&k2LaqzxE>>KK=`j7Xapr;@2pH=WJ|2COaZ^1 zs9qbTnsOjFsB8@IPcxFJsJP(aDSQ|>_Cmitqob37em6(N1!_Ju2vFfakV4hMWr0ha7Md5C_O3`Bpw0Ff z^Wyvj{`OgOGUjvGBtGN=Y-P8? zHNxSDk*Lq;q&w=`gZ%kcF(8bQWc2k3WSG!*v>TC^g-JOc+XkNYg+cg|joSH;nkqSu zvsXj54c5Bb%d8332P{HVW-X0TIuoXxH5BSLhNoiQ?-axI%^IgI;8npXoJ`%euPmmG z>S;9ftAs%Gr1-s~x_=#K-v*d0vY!WPP`6C5JSO3>lH{8>WkKH|*dHQpUl7NvgF-9c z`*&zqD$?I>_8!=BO53{OW9p+2G4MZmWMfQaknm_+2yR=+C($UzH!rqHH`ggIy!|SA z9UtC=V_UQ2aP)TSHgmJnmXCE==dV%$!2~0Q?NZeIK#cxmHX@`bLAxwk1))|zD!@l8 zAs8ebE9+%NL%-R{cz=1o?<-xLRl>M#e~AOj1OOQB#pz^DVe7O3FH3zY zVe+_V@oG7&ei1RhpA#$kVRpd1(NDWtazUGxJVuVPocjX1nEycn|0njsOb7%|LT_(?nCw{*7;$gc91YRdkdI?U z6qg>(&YjHwMqX_L+FLPoF8ff{R1EVq?UGqysjR0AJC-~nXv{B_Fa9RC-vJJgMj-@z3;2{tL63)@|02oW64-5+C(4r)HDatCY+epj zIUdPugeNRkh8ntelPBtf6gnQ()9(%dA{l9f`xIqEs2uXbKhviuW28S53|yQA1;WkW z&l!on3T*nG=;`oG0S+L}hSF&c*v|(E;3ODHoTwFQPO$pp-!XNdi?^k0#lOkh4>VkzcFdI11e&T|n_8My-(|Lc*W74VC(c*S z4RdgV&NTw4Hh1tvQ`gOCdPmRB@47&`S*xJS#e>UcXU`p}HdmgX24~+N@HwAg5dEJw zu%Obz%?H*Adg|+SQ6(XJoT=>DJwxp2Ki&O);c7Yhr)W@i_tRJNg}iVcO>S3#4=2~J z!6x-CEdd=welGJ@S-Nb<3w}vd9ZCCJmTf%Y1lc|Q@XYHUp%gYcWV6ZTjG3aNge&Hhb8Hx59u z*z(I^P15E-$*Z|Ec(O74+|sq#eirty>xbE%ug^p|PIkijxY$&r>Z5~c>;`qpFhAFS zW!}v}7d`YfSrr}$trCb(a$2UJ(zY8W9nf*)RoziNUcXiZlXf4b6L73+++B4A<>#17 z-A~ZsddYX^2;G6Yh%q#cE53>M&>^Lc+rb!p)`oBjG*$y z0xJ8?SbGl3RIZDKI@S%S04QS?bmQcOJ;BMxG+UU$BwLp#aTPv?C7+Bys8LM=2@=8DnJZ!mSJYlhDE4%l?s1_NFm<9PLhkp^gI8qfX7i9TRu+JL?}NSS$Plz-}d zMD9UmlFEU~y}LIz7awJCU)!C(gxS^n)G3HEx!tcFTwhzcUi%>SL!mG<1=EDhEa61Y zWkeZ9zS;h8saGRhQdRl<6zxQhMj1$E^r%JAQ>jQr`Hd7WMc&E$OkaabhM1DaX}3tD zf7malb)RWt%@iy3#WPZD{FO5R)i&~~C+s^nQ~6nBVbEVPyfNW;dvHpvd;CYTxP?yJ zFRC3k*{8a$O@`K{&j1`t0A!tsTbGy)uWmt*)B8~+y`Oir%HYxK=%H49M-j8*8zWq(*JfI1=F`;*1@OCpDLjwM)iROdxo-JuX6VU+m_L1> zd`m%Pt50}!9WAG|*&y0soli*#TsJht*<&mgq7z5=J6!4HS9{bL&xmaO@jF(%Z)}=u*koJJb2d8WD_&rstRoo7M$;>S z-O|fEqE!}ERD6g!vp)WQd!7;nVXat~eS&BM;Mr<_aNQK5DOpm5X?e^}9wa*$!6GSD1!oH*s@n`!@Fj%*MS zgrht2G;jH>%#>sTSa?~}U>Af(U|D#7qbYhTK7=DP4cK^Wo+N{82HIgpkZ^O5dFki{ zM8mnuvYD_xXcY}gX=)@wj5y>mySRZ_mA8#H&IVm2M9{DZAAiRQLYzOiY#RElTc9RV zj5;uxbv#dX-MEVK|5_4+$`O?(p2bx{f6@jEtb>oLl44I1oa zW^pVslGBpO!ss(wl-)bDsE`7XeCv*Rp0*P&Etl!fHiO}33*SK0+>OTX0kLBSu^taF zm^h7k14@byNyRi7hMSCW5QU+u^A4C)2Grxat*b)qJV~o^Zd-u1rg@X5lbh_OD6R#> zLnQc0GGKVBveliw8pHG4F7w{L384X#`iW$*e}cYCMbzr9!a1=S;=Lb*ZS z!sPGWSaBiKU)+TR)c?@Sq82BY8+;Fka-I%<3N!TD4`fjRmh?Ao>sWW9HsXd_MoW2~ zz-Vf} znIW$vJvw;8JQv2K`JOD3gq^o++LXtx2rPU4a}v9Sid|sAn$QRsXs!LnoRGjD{kj5K z1X_Nnug^~kSI-b~npk6J7&w^F{)1!~5cYcqT(kZ$klk>hs` z4ly}U4KMkTZqkZaG{e;2(9C!BRn?re0i8vJ7nV%(?pi!V>fM)Zw%s>&5#9|4b%{tl z5l0SY7F5`5M`y&pui-bnYP;3n4Ih0cFCIKP-!()gWuLNV>uH>KEzJ768X$&1weYV_ zkcweT_g-luv(Rzt?07}Y2I!r6U4B?s&P^?O?FY}y)KmT27~G<7xyz3`^DP<^R)guC zBl0oe5ro9RL7kyiRGy;9xPQmG1RJ|t5?fLQE!o_-Omk%^AOsA40JR}V z#@9`iH*-U<3eA6p{7Ir#n)R3{$BY-=`}3-&8^%|xi>rQ)QPzy$WzA;E_8%(^d>Wcs zUDv)^b*w+(N`1bb$Y7;3%H(=!&H>sO3a>wd78-9?KK0N2Ty)tohN!R{ON{V2o_@tU zgmvTW1X2o@2Rl$`z1^Rsle@aF?+dw}o)j`8B=`0Uy6HBs5_8zF2wz`KI#!xt_fCqH zH@4MZk|<5&B_}d1-;fybBhJ!%OlIj2(&eo!QPP-O&WJU z7;{I-t>=QzNONSQ1y7Rv-YpLhCQ@nh7vqZ8Gh}IUBvaK$z3m{cpN;42W?u+a((p;= zVs)aeC6;q9Cj+Fp?F;^+{O5K~rzGN|DqsjwcM8EMZvUn<4A#^;bAL+*e161Mb8o31 z^O7U~g38j^dt$f+^Os7+_mWceUvfyB9R)QgyN}`stpP49Gfqo+C*D*LH*J?Jj2fT- zEMskV*H6ult(HR0vp$wsZ2TJS{`0wex3@cP%^VItCv^-&K-36Q{w6v?XczXd^G$)5 zumitY25dvXQ9ap|es0K0yNtD?NH8Fu*1)(w5UX?%NkBxu+HGue0<(p@nd-tv)~sw^ z`NO*onqq+j6OKf)x6!JM?IEPLkuOV;rcF&{%Z#bW9yik}vxLWW@c@BDd`z9dvlEj{ zn&5&eMx%rPDU7tAd853|kv;(gaq7K7XMpM>y?0Tmv|8P2_pE#rOi!;@J%|M3F#v&o z&fB<3V2AjXu=8c7`vsgYT`^TeHS@^$RDaLqYRclO4*CJivCp?$wwplxYo4CGo7#hL zd;B(I&yVV)m_fa~%N(W5tP0yYwWeYRh6U~Uv^3KzcEw>ZwqavyV#k^WKBVLh)Z)j&}H09&fxID2}@#%`XSRn$kZgIhv(-}KWL?(A|QpnLoXfqRGg z%NxEO1ouAqJWUtZe%qZ9nuAzN`EgkTsxl?o-M>o!tgU`A0JH?xO`;AgtWa+8VZgt8 zjYjCdQ1s3DcEP{P0tVroR5S(OZkhYtx)sj(V&XgbVnbUx6N_*(*)se?Ofqzz^Usx{ zx-fHZ{Ui?jS*eh@Dy)8Qx`sbvr!Q$LG2{g^&CP#v)g(*oQP6eQIXGA&t~8sLXbogP z=`*f(x6TgRgqIPq0gX&{J(gNm5sH6SspG;rx|1Yha!|D`fAnpOW%g+I{a+Zk-^JDc z%jy2xjr?z&?*CzX_&xS3{aSv5;a!bRT~Nt2wUo6>DsX%*K$; zh7sO5v1KZ$eZn_WiO~-l*NLv8my!c987QT%_}B;g%htndwja8g+Oe#Bz$r^s8d*Eh=oX+#MUQSK<>qNXx9x81Fp{LpYpR;{Hd(_hQD zjrfWddSGMshp;MD#ts5D5km&1$@Gpghfxi(&qWc5gypv`f?2jIL^gN+zi9@|QuM;} zIEB>X)-@$zB^u-GLKHL_6Lw7kBt*Xkts@fIW#t6>G)f|_d|mYcT+0H2b{g}GG59sY z8*u%Np69ww7lV&OhnG8#c5v$t*J_J}0^gw55@nMHk&OM4rglQraKuIb6w{K})Z=52 z9>+UbQ}=BjHy_I@)Lb1(G;?e0_Oa83idO1ZwRoO(5s!3X0+z_cV#yK2O2~1$iZ$YH zv?a$(bO-PH1ctHs6f5{SX^mw)#g67v<{h%|6!yVI-cKu&4B+hj#-Zd$n?(@N+gMB_ zglNq`-hg7wp)wc_ z5{oR+>I(9D)7JRH-86KvhSp?}Q`G*^YfnoH2x8KOwu}-9#)Y4W*1?zpEuk1G5T#f< z@3hR~PN1E(5jB~4_ZawSy29AEt68| zvRubsue}4e(3QTlS0-d=efU2`D3&Np+^Tntycc>zBWYsqbxSUgDOMDR>sst_3m z{<9q=n)pg0Oz?g9l%J4k{er8gsx=#vXjjWe*V9~iKW%4GYa!-DXvPXS7&HAW4FICZ zBx!yF;~jx^8bmNM@$e#^q`gmvUEcSZb6!kDp?+7*p4*{p>-3#)fDO!1KVoiIH>e&$ zHwR`PF)U#MeR9SSpDhD2(og2LP54>-gF>o8s#}e#Dgz518cMB6J|bF#KXw*lGuBF z$fNWymfPf?cQPN3JRc8QT6{Y#xn8%I`Y?|+7#Rh7kigSpKe)LH&jNj=gB@kqq7bfw z7UKf{sN^_T`MjPf69mWv>I)0%9%~)0z$q=%S=e#>4Rn{DaKLwIHWztKa=TjF?I8xn zo;Ly!%xf#mnu)rNYirVxIi)}qgr#f}%XvMFJDk)`r{)8soL0GD2sP83@%>a8x1m-! zOV!d@Oti6cv6uA>CM_OA3<9lk-|`=hJ__+5m$V*AjQKpkXL_Hm%CD293h^v;=PL^S z!MNO~c)tjrtYmyGw_k5Z3yeT%0JK4zJfKUhOqWrFsVFIFqNe|&mZ9q|pp!OUP+Jx+ zNm$=$>uUBH+>f!??D2r|Y7l3NG*C(mTj*}E;p=I0|Iu!@@T(5gx83=f(fvw$YPiYT z@Ol)-`V=6IIMhs3qt8Z<&S@Ny_33@u`C|etN5TMVBQcQAh1+j4LklNL65^Nf zj=G^~(PO)xqT3s_%m_Nh;E()--((tQ=DZ|3zMejAFTD>&N{U}rKTS-UL71k}ZpM=1 z0bn8D4M_TT7!Z5?uZJF=CPfcHhHa%$83TW(aE{uKFzOqw^T$Z>71V`Dtwk9E|7rnZ zUaz|^@L4B{kV0vWpo9|GwYJYf_g$U16j&aVOw`h_ARiW`R@)mE{nYN_w%%z3OZX(T z1vim2zgP~Qs#eM$ee$__i{Breb6?Uqcs;{SC)s_O?F<(BYCp*9*{yAK;|VwEF&R+c z{k*aA5t~bq^TyWZJWn64bjPg_ZNpH&H?NbQ@3p9PM$n&A6k1?z7I%-&p+4eFw}lg- zszo|VUY=QMcjRnY{ct2Fgdn^q77+IY_zfolsbow%e1BjHZ4A8ePgYtc1J-ESDtR0+ zhg3o97+NOB)YRmFjVBwq!|^Kfdf5?Kr+6M(>S{di;Jt@(yh0e2@e9oEr-0hAIizeR zk-L!3AqHb`CbT*K)o7NsT!Aqn_Z^>}n9|v<#gG+wjWNWA_|os7KLq+Rn%&W#W5NBE zY%`7|yv#e*_3i_*zmHP#A?SfeNZcF&mhG=SKbg8QCtmjYw-ZkwLBusLH2s+=K2}i0 z2@zID?%u@l{S^XK30wg$*kfV)(sdzTiM`<^mfB35)oD1GZ7_pY& zEmq(z`sc`At7+dXuebNd16C)TPg^1HNDsp^OpN!3*!k`QDBuFZ?Z+#Y=h3d=?MJ(b zkLiuGY**{Yo~dTRGUMng+ek0Nck~@yv4_#FGtp|~yDnRWB_;sW$w%6_Bw=gpyKnbZ zwmzg+?U6Kt+=g&7WqL+I`XP&ka?{&Re{8Z;wrA-X*}~dQyTl-9uTw^~g;L8-6_%dN z{{9&TmFA64u8zj94uHr*!he4cqXpHRfrG;za&s5b(OfDukIO zo3ohQQCmkOuC9+H%-ie}bY2mDGbj&OQo0TMiNDL*3yp$KQT81{@k`Epuq)$usaJQ)vN2pL~0ox@{+ zW31n$w-O_Qs$061w@g)|Kc;(m(&407Z=vJqt}4LJ*t^tA#SAK-AlK9G0|$) z$IhKhR#O1$nTVn(@q|{sF`_z)qYo~fgDckxOY0ffd+ zvmHF3*o}o6cEc+pO4aUarBPu}#hG6>=_Ae3z(LXK6cuE1v5jDJRBN4yiO}++(@bXw zkaHcWIYvYu8p%tC*IleI2`IHsM3S87A2lka??Nl~!GIFTE=P??*G7)Xk3Yedi+**t zwKSO6I?zC1YkB;?%ZKv_D~M0*T$;2zK0;{|s8AM-Vv+n!y?zQse-yJBHF&s4r~s~E zY}c&9`QAh5He}Mndx0N1{tXk&kg6FdBo6fLg`H&qQZSLaMCuoZqIn}umcY5ETzPJ0 zGHlKinwb-JecDh?fX*q0Wb+dScG)yi$w{tY(4jZNn|q+2L9MdD_OcaNR&eTnaL*kO zxWV*a-atPEH%VHk&HI)yLUV<}2srrY-fdk^uMC0t;LCzHOI9uXLz+UjQ~$sXSQU>M-j@CYdm9^k`K90LIv`anQ(iD)}=WB>`Y@ZYli@_{YN5u8MjyQY1BO5x%RW;8$&Fboo){}_(#QTjz! zoWYdjUP3>9uC%#s_>S?K*j8VFSVI{vf%Ot--qGWS1s`shxjSL; z3{8Y76tJd5D($uqzt~C^Ea0nTh6Fu!<9zM!S9{aTR^QNpq!^6N_DEP;(=@YA_~73q zAMN>xfYvwv`WokU&lCW49-}wapYj06Tf!j56Y=`~5rWS&scPTvfo^8~7Wo;0hVRED zpuK#r)w<<59X;M(k?_N*r)T9;M|7H@v*qo{$ZGiGVgV|>v4xJ|1cmeol%22wX@52; z=qOu>YcfCql0rb@pxkE_q7b4?G&zc3w=!HC7|yBOm3;3x*;4nE|R ziidfKExKUPrhNipN-Vap1(giB8Xz_%)VF`IgmNiwb_Sp;%zh15H~;4j(X5~NYaB2a zm>AW@q2Gj^i6Jx6cCZ1O7aq$5sE&J*%t>Ze0CY+y!6E+p?Kg=rVNOy>>MOvi&(^Mz z)JoO93q^Ra&+V0ymS&@^+K&6{L9aWnRod+QRRi*^xJj~PF~BCSr4MnBGtzgxNSC3P za({MBP=*i!n@8YyC?)a6)wc~-e6NB!T+ppZ18YE^z@;}K5%^5@@*fn=Om4w^ut<%46fll>Kt`bzeP(>Mua3}rnH(K2M!smB+kNFV4Kk8cNF9Yt_{3&!(C%Z-)80R zePAF0$%ODmsLjc}HLu92RZ5iib?Xi@S%dbtXQs4Vhn(-4#&^bcT9ic?&9YcYKDrYi zfFCP_E?;^?v^A*Krx7BhqW%E}nb}axZAo*{U%))$*E!%`rm<~4_}LGzf9W~g>lCw( z$dj|bk!y(1??xdPNmch1*dO3uK4;Fkl`{V zp12$BW`N3=88K}zi3Og~J5>fRMs-qf3VrXUdUfkSS2;^r0RpuYC{9fjGwHz%x|8Bj zQ679>!&E7;o+vx3=|PX+gRrC|J+{&W-b8l%P@ndrfzpimSMu*jjU-`W)wjpe(c}|+ z>#=P{q<`HhkSbP&-4QqC;xijm{yobDoe{XfB=zXcJ<5bjB$iO9V*>uWP10<}6c~Jt z1{nk?JX8kp>20#jY2@obo`E!IZTO;g``~>f%djWeor`%A#IERgVYvVKpYuBvep0&( z0suh1?*HeT#Q!zF8~^Hv{`)3@ZRxl;GrE>;fk_A*5unLfOXEqC=IAt-L@{ituRo#@BW~-r$a+L)5uZ zrrIEWyFS}Fd*F|MZ$eF#Ym>oXljg8T;)6$MTE5)i7iEUVL4(ypo+xuCF@o# z{2+7LZ-8FkT=#MO&y|OQHObkuGC^8Qzb%)yZBdgq1Ht%j{XR~}r{b1>ew6l&j zV5o&?=K;KFH;g*hV-OL7OtoYbvXHrgPjksBP8&BzA!Ur>!V*k4GmA=b-#B^~yI~qM zb7)I}kbPUtQdtf?;Ehxy8I@Sf;oGlaI4Gcnxva&r&1 zL8Dd0Vn<*Qm!wQ7@6`@m37h580_KIvc)=)VhKN4}R0xD{bGEZBJ)^0_2*-UxlQ=)v zT62f|vluNQc_yW5cX(>vE^KoJqS#)m7YAtRDMZk&L_d~2+h&XjW>1(`%+=1P!Ohae zw&TxU)x+Be7#vWm$$7?A*yE-)(TcQrUl)k7>`Ya99y!!+My!m`@*30LLWaaC1EL}7 z34qGDpoS%Nf=Ct*tv846)Kd_XIWKcG}G(>g@<1D zcW#NAc4*@9P>r{lNNJO^aE$ByWgBkVAIHuw zm)0+~Y%aZRXFHxx-&{2Ct%m6xGc7pMo<8M`s)kdajPLKp6^S{znnjNpx{SY+GM zD(zrn-Hx4kvatr%k0T9|{DTd82qgqOtc~vJA>Ef#RDf5{zK4T=omkj>m8ZMRYHegG zQodfYV}HtRKjjjv(mO|_JXYw)_kL$p$iN4nF#-kJOnGJ9+u7+nU5Ilv_q7M=Ho$1& zbCgo9*!}XI;`wrwO*c!kL~X~ir{>c0Io172ntSBqV{&rwBItNwQfVwo=6QtJIA2#D z7zMP?D?PLFkZroVzTLI&^DOIn(VKV);pL9As$8l;z{mZ%=2h_?5r?nlC0ghqzx+!? zk(PnMr5~03@q9Uw(#9D1x3;$ykX(ApT)Q^n@F(&dk}32gy#{9MFGm$FmzB`II*}D1 z0J0|hqUjhBrZgu?t)MEt_FkxoDM9ic3(rgjW7)Fcx_L}D0HtzW-xl0D5bh0~?~#vp z{%4f|9_y*FrJqxIi@|3=%_N}H%f?Z+l-ALAVYn)Q29*c+_e|<0bkC0-;i(w~I3`Tn z$94BkGna7}7mzl(0nFAU%wqnZP?c+;h_}Tc#qT&C`dAKLfG|#8eskiUsDc5nBF=wf zca2}lX88+Ya_0z5`6T#l%ku=h{@anwuD!4-o+Pag=*1X}vO zlz)8pWE8u7yOP_tjX25C8OdBtK-Y zDpzakZMW_$1y-NChJ5>)p33=Un`AOBI%#sY1r{H@Ss`f*$)C$4JUD_4lQLC0{iePy z=b4-g_l!utQqwR+jVy}R+;~%a0h32T!1@#SO~TSrg|aok_!9nVCM!;EvOPjb^XCT1}*5?g8v&1vXTVdi1tX}N&X zM-2yU$NqT}M^LuWhwgJ89v0PPvL1oK?7aZ&ICZbZdF+SjV&>IV=v#z>lcFhIBB#BF z`&1vWn+PkjjG}>PqgeMkbH?Qrfa0Kgbu_Jg8EkqVoC~J^_`J?=1bjy~e2gS-t6lG` zHp14!Zm3dcdcffxhJ$sus#=c1c88xoe~n;*((gSA)6+B_;QMs`-MZB2U_q#2iN+O^ zU!ASfO~=aIhU-lS$5-+Ot#h^9{5std&Z|=a;J;H1{P3D$D4;FLacC-=um2Fkt}MKA zXuxqG?&9dT?;B37gVNL3vW37AT?ewNlM|5qyj~stl8OLR3PIH?juSnX8CK61;T4bg zH)Crn{*m|+8^HeWsy8EE1{PxJ-V#rnu^moc`LY=aJo=+36!$h&c|)GFLn+yP6U;5T zD2M$<-={YyH{G1zmlhB&K}hz-CCkvDKNON=-hS(k9-#5BCxea5n9n7gq@Lyf;OrfM zWQ~?}!M1Hf)^6LjZM%E=^1X9r&YL?^5fxPxu~t@8X8rkR=KAtW z5uIBmpU%+id?83HZ&PTG)zF2{`*Sb9%|!Ls1VRVcrIL1;Fa;<3s=zsNlxlU90YswH zNx2PZKm4>}e`@bad%c+-1{k6C=j+R@rxb_yDS7htJg|Ye5JcE;*hy3Vy>c{5>}#Xs z(B>PNVgFjP`Q>IeJ6U6%j74g$3WYk{$h-@D2r)J)tZ6zy$D%!4|u8(ltVE#VRGtBof8Ts$30Ru)rE zu+Y|&?c7fddrp3Sp1XTfIaZ@mkspTDd#X`gShyECt6ZxtQdUu~5*etOeKM}o^<2Ek z@sR^vbG1(+#J%tqP?Fc6lD_>!GWVs=zphp$IY(hRh?+)VYCDtHd^g+$=hoy_Uq4AG zYIdS23aoiZM}3Hknp}$;?V@MoZ)Zjf)zAO>hMOio1%!nP1j%YY2XPc-xATf0aVt_M zI%ZsSxj8)NK<9GTWu-n2=J{pquyK=e6G?QoOoRFbXy^t43(=n$z-WtqB}ZHWfVo^N zk&X&=4giILBrIq_I#9V-XZ9~8q-qn~%^KlaRyR<2j|S>IY}+NJK^M<5oc$JWn;G57 zG(`M>kyr#C_nipKn+Ab@rA!!Z)u#vX6A^{5TYK$)Yx!l=?}M}@<=!`$Mt%4qY7B=) z<7ZZvq;wqc>jO0fHP$?H?}X{#qfb|d`5 zbfO}hI+}YKX7ZKCi8#f-fme1!&EQ3pcMPw`f2)TpDG497;^Gy_dYbc6TW`_dta?SD zvCDQL&sxzagcoySa`GU%)bv#?yq9bF+Ug?92@_bV=fMX zlgF*eE&ZM}Pa+zK%4tzV2xqI2volX9ErJGA3QK^j>l1B!IfFM8ae^j3WGYBM5>qxA zZ;p+QQ+#yryoDXuJ3N'h((=3rZKk##eO7<{R{C*00LaNRFBZ&}~Jzng(L;l(*A z5p4O9;N98$_p+mH_0V=90<*@0s`UwP$T%CsjuI#xdE1eaQtc1$=BhE)`_EFMp4maP z`DMwhl=16$RqquN?#nU7<(B#Bai90&HrW2$)edjx!_H&!4TH?MKmb<;CIT`_j>{6S zp8qm9BDbvsfbdF;N?OJr)+GsWknDhsr3v!^NSLK2c(JvkPZ7rldAQO#cDwSO@e4)Z z*R6lH2%F)hqKm4Fcr6x2njHWscbr`zz%IpjVHPFJaw~KhsKPy*pa@9}hvG?1>J?`{ z02ZDamjLLjda|emg_q=;#zh2qGXnI_gN$Y0R5O2AP6vDQ-G_*`4a$@+T@aI*{Ht{; zT#F(EDvnVkrEX46A(n#7!RL=QA`yr^71?qI1|c?UqkK)To;um(>ic}9!@w115j)+E zvUF5`(I37kjf^Nd4?zN7ehGq882 zWVsycy?$m{JAa4qbKHmhz3lclawez#akBws5TWp|$SUjyO9$ z4y2j2=SU9}L`eK_ z82)1UpYH%aK(WmCUL(Z=2Q7_)hHdse78j=BU!GPD}dM0T~Hnt?$a$K+z! zF4>GgXsTYuU2_h6r+)nQ<>@xR?u)H=v+K#Mt0pyrcZA#Ff@Si(!BHR$8RzlHKkMnv zP!_>z2ZDo=ky+I5(iu$V73?&vaAWp|8+p2o_|rYfL4Ia;hbce_#rdq7@nM$(PzT3! zIA1D|9idKu3a^5EJ0Mb}Vx7L2aD0bTP*gJa?)(tgIN28;|3u$de>UE;_k$T-@P{R*Q>6MQm_l#MJ1dMpt?$3Fua z2P5Y9g82WxZrx>H&lG^ZRl1^ zo`7T`1tntkO31s}<#oMvyyfNX<~?DkLYf-LkBFR%OuR{nKrLnR!=eVkKlc4Vw)!mW zfHg@}cshb0vL#f@2|fl!c@(_5dXGM(oBn!m@laznH8~}TDpLoM7@4V%AxcVHZmZ;w zWwFZ4mpM%%gBea>URZaV382nJmhPND&=8G)3ZS#F`ojR6k5pv$N1*qfpLB-V2`C^6 z4J~S*FA`t{L;t&8TO1tl`Zkl18KpqtRMAMK#hgDLKv~_uX37B%DT*acmLk57S#KnnMq+_7MYydUsZ}40|V7d#uZb&*s5Ecf#l@3#+7s+muYGp#X(xBsoAh1 zUh-qkIXD-c(tcBw@XhUopi_<+_Egd5{;?@Z6b=gxD1wkmiMR=Aa7zAZM7$y}I<;r# zU4k`(NcSrk^(wX1;{J=vU2;?8pK|XAAL)ziiOba%Q#)wRYL6Y0a1x;gXLvd#rjv_P zU-YB;1&E#8QGQB>u)|pHk%uW2gJvH=-gN`tPV~mCUsjG~Q2j@wEQ~pp(qH&r^(p9Oh?vJSr z4nzu#2aG>_3~PNSfJSwRe0qjWwa5Sy#+V7&{KWIHRYPa-;5BMFivzocG|B!pyOLF< zhgCku+}_+TMh};p3PhO&Mjo+zgQBsTn1j&kyDjk8ExtRlZEqW2;|<22iTWgvGu|8w zSnE^Qmtt0sd_+k%lC&bsEMV%{_AJeR)~O(eKPXEbIRMwOBxR+xaD?1#!$%o-6Wwao z#=m7*4F#1#BiwnTCQY+LwYPoOTA#|2B2vYBm8Affe1FBV9IvgM8-i9&7MDO`Jc*prDo}R%d4x+vYx#6NQ5DZ484aYg#`Sx{AG@mYOTYFJp zB|wZi(X!dGpOfvus6$VcPy4~ooU_!+^=UDeWmnUe4%SE)rU<4qyh;U*=d0yBYqB#J z(5ShD*G+Oek{qrUxbNBwa@C5|frQ>9r2UfNE#9cpQ9QINAeMY-2c%R~Bs8PUCddBn zMRP6_65fHWbC+JUdpD)BS5mALu+uA??gHCF}Vn+cr4qu5w~W7;Fq zsUM8yDCugl+BU{)YspVI^QU4sLlAFigZlV%0-^+QghPnJ0u0qgwST#M9GXuNc2>yO z6co*`5J`p%6qSB2A_)9o-uCFy_S=e!fkg%nBxF`UHrZZn#^<}j<<^>wsr;%7iO@1U zPVGJnrc^cefXdFz_WnMBKu`64dHft&>na+2W5K6c)iub~dqBImrQE!m+-rFfr=fb) zHiU>a)6_hdSE$uQDle7$IbYflQNFgE%}m=}r|Wk3!;AaJyn4HBvaqROd};5GtW(QP z3m6?*NjeFzwK%n*U;^SrEK$^HTfG3)0T9^owX@x@K41a_sqHtv7=tek(3RF(wmyMt z{)>m|o)2MEdWPxJjGgG|AA|6I6Q=yTcFrfh-ZOqkFJY5veCMfKZB7Ko&h1UbklwjR z`?f#RFVLW<1tR9OgIHu`(%trTR zFQ+p`v970YW(Vy|i3BYib;?)?`b|}jDp$Hh_M7y5WEqtjay=%W(DJo-}LX!)WRYqq;Ir)AO z1~1#4B9N=O{06Y=um-&*$TEBD1Sw^9M%8H)UW2YzKU|?L&6AGT;bHHT-ud}*Av1jK z!ynH#!!zA2cD>K>wvN@_1+nVK(lTD`jXy7Wwd@?WzRSKCXgJT4YMC^U;c zS>QuMy^7fUf#mF;hRzQT3ZC7W2(q^I(b^FIZYo@1Q}YjG&5q(As{* zeJIEPb>ye>%xT-CkT)~Q5{tfB< zFZ39+x?|39d6H=exKa{G3l3{ld%r>?K`Sj#6_#mA;#&Q+p+11z6W0=9jYT7^D(Z-EqsSYca-$JTE$AfyS zC>O;GPgci-y;~-%pDNvYRnzI!0a*F<99e%o>-+V$H6OyNWE5JL>DXl{L}9Imlqg-* z(7~uzo3t*oe(wKMi@Z*G>YfW206;j}|F}i|zhkHW)w8{*u4T6%isD90G)$xStZ!3-h8TlAaV@amZ+B5m_Y_b(nX`!Qsj%(#D zrf4GL0QOwDbc1lzELwu`UIF?N4tI)T(Mibi>RzF(gzCMnIqjn&z+oFgqYZbj3*4&Hg>OW#V-mF7dBN>) zE)sm+Ulj)^2x00tZLM(K5bQ?=GjUSV`>)xaw3>fqA%xeQjqU~_0^yc6c)yEX-hK#c zJGlHl3(&Jrl?b1QuKh#Xaqu@{b9rSL1^d6AoacnS6`hZ+e^7#3NnjXL%Bkx~gH)vK zwst694-fwSj`k_+I%Xo3RO`=p&9-@4zE#|ddbPfWFND_+z`c42+d1@m$y~zYy;k9L zuR#20yPIycB1b&nj^>i%s8UR2;1W+@lM!GPd9=8d%L1*=vnK_=L$iglOJpk?(G|v> z2BSzRBLG3;p*KUkx=GVD?k#yBDqY6HOkgL;fvRS&DiO&;_4(Ii$S63{UF4OiMt3iE zpUiVQP`cPR*Szs^^z63Z%6#@G)j=)0p;bUv(~W?c0tpp{hqvh>{Bl%kRG}gA=7v58 zrzCOd1T{iO3<*!ELnaUnK;r^!*EVepePy-#=*dW%Z4eDl_|`C2Fpw#jq=KR?kGck2 zw%RqNg1yie*53*bt!D4ds>k8AsIa+OuEel=dOSS{=Rev1aDpgkSz3+M6fy6dy35!F zoZT65=6LFG7#65#sRdR=+_PPanz3q~^HCsD1`Ph5Mn>(+K6VjH>Kg&(?Ug>a)auhNnof1=lg{~Y|VVZ*pww>Lhj#gAGs2zHSrWh#YN|r+JyF;qp2U@ zdKA4$XB0p$OLJjfZn?4zGNw^@v`(DwbQKOsNFFO5h*!eN2~kt6?MuZ;F0_~At^i5r z=2_?%ubUS-5z1W@yIB-V3~Z21*(@t#0YhHvGXdR(!QU~~+|$$3^?34KTy@Al>BnQD zB{ywd^L3M&j7x)Kvj-zdyRwD5ow~fPc0DlHZ%t|yQ5ASvSS0$@m4;_78>toP@+DJx zrViDdzTeoOVU-`|I6Se86Dbv4ES+Xk;a*ZqZ%#pcS6-MbUL9* zSs5qJ;qorCpNgP<=7!jXC*d`y6go$xcQFnSFHc1_R9CN|T& zwuOBsQ9DZeL^!m7vUza_R`k-NMDeAR89$a%;0WM^KSbeQPlbmU7{X_6aWK}12Opj5 zGtBT2#Iq@xA8;B_;%cJ=Y#1EhmI+f)<<#%Acv&=`xLf|tx9D~poC;C~AJ+cCVJBmx zi0&kwtiA`f2vRtcC7Nl-o!X48-h9Ge=nHj65iS*HG<~PridoZgzVutvhkvb-*;#kW zVZ|@_2hj)ORRzNU4%8e|P>`L?JA>nK&H$eV5q${cBBW|v#9^ZmlE#A<7qV+mvxMA7 zl}}I^CZ%PVXB(styo5CRMuswu+2^!`UcTJvG<}hDaHo_vvz#y}fn_0p`&_o%Ak>?) zmO-_ST($tnkwk8Txie;|0^%Bsn3|8p%MjF#qd7v;8c&xV`C9zCRyB1)C_z!?5xn{( zFzcF5&F>X59iQyGjiYbk+i-VHFX}xU!Q@S2x$ z8<+9mpSh)jix{t>$AU1;H1c!X12wWEz~N)fU<-YN$Bg*0exptr)<0z#owmO-Ou$?= zP;qH#&92gj2QM6n6;kJm5<$>Jfyi?qm2__8K#YwBbvM+QNssMEM z3z(kIS4VR)76|*QL1)@jF_wAK;YPk+@A(gq#{W~d2KE1}TjRfPWd8{F2p594^ZsFt zu>k%b!#(V+UCbjQ>6%IwX%Ey6Q3| zY|=%@vy-m~GT{C7pm)^+e%J1-;033sH7~G&i9#Qy`|uEj%2WP)fH#F-GUfCCfwu?_ z>OYu@6j3>L0U1j68UHW(r*rn{%mz9u6rcPGbuxUh0@c4GU<6i9u!;V zVwMF>DFx1a@!jFm8ZAKrb0{kw(^%-|ufO@o33m11$?SoYR7&pApRU*jYkVLdgj9Sq z8=8olexkfAl5A{Ey1`p|Ze%M*T!|rRsFAF@9cgSOC9_Icg`NWTP8XFqon3gZa;nF1 zm-RXJcX1COUf@G$drSSh0AP*0vi2B)TsNn315K`*WSKgj|1`_*nODhw=%+e_ME*bS zzWFb6+8Ud<|7Sz-rqad|s^7KrcbHaN4k%c4YVqibRjTi636sZ&&^9+-oN9ZAmrZrC zxEjb9z0e$z5Pb+(bP`6#45x$X8c+^vv(;a}Ny(1)WWR#rtxD8H)px+5ft*oZ|6;GJ zw&UD;F8pWlq=5w(x#8b}FxrVI>r~jnN|x%2bwm_Od|$H%a>lP%xkI5QMq;U}$Z1np z7_=qB97ZL-k;a(MJA%{tv;<&Idoy36i z&)fvXM6ry%-=-NKzdVW0D>Oiz!D7RgxkPOIZBvPhIn52E+1vT}q z{8b@TGXLX#eu-(&R_S#<^`q6}ci6w<$}RpIg3M#4-JcQ~zm~xool4 z75lsW8xIDzb^ ztCq_!Kbg=TCUs|jfi%3AFuHo!x2u$K{V_rtQx!_uQWA^88zu_VK`C{zWKd}}&Zs(B zS`(G!!-(L~!0MpZi*LQyqY6fVb&wc^ri&q!(i@c0)&|<*bcl!2ShGYh6l}YzOV(w+ zgsBtAW(M+yu!`7fmQFWg;(3@J3BL2(&D&Uns%N;sMIOIvh5bXx3U&Y$tn@O#P*2}y zJcn|7px+7Pu5&`Q629~0%6)@!!Qt9TTqgJCm5+DRf*>eMN|{2|ktbD?eK^W;k}*q! zE=&yVGuuV-X9vX<9|o|LQNktzM{ei@81BQV5u1EgN!lyzbz>$0@|yrf16kvOYckz9Ku+LH^4v>yQqKR0uMHXLAHW&xoN%w*?RBE4{B5; zUml|HsQmqs#JnIj#;yppN-bd8Y<|jX(zWHsdtiIUo<`s6gCdeP3R5;+UE;lg_?d8e ze85|coK_$&R;9UE)o!B?g(RRnseFTA`|!Cssi$Yd%hAf;aqG_>yc<|Fd9+V&@S#tE zHj|bXch%eVl< zQW?8V4AIeS=_-jFd+-Y=OHTx7+?u&yZ|K(5%HS4A1!kpEW6I)AL|Yz_*lXu4S+x}a z<9)pHYb~J1XNOO_l^@qh8g8lf`*FDU$x&N$rmg4Q?K_DMVFx>O7ek|^kMd1s?&^lz zuG;3k@_8+)a&o_mNhANej>G^+Y%yZ7P5?J6H@H-#KPU?lGGrK@z3lD$T_0yw$b+Rh z1i3lt+jjf=xR%uuH{ifao&n?>s=OPFqzofZWfC33oRe%aWN3HH3W8a|jY~R-)TK(< z|1>_kL@-#7lb$8Frnv=OQ_l~D^XAG=?4#-PsqgN0ZLgb{+d4G<`?*x@@yU(eAFQ$gl}ls9=J0ug?E;a+3)895CQGiS$c=i3a6FGekMn9_X~#y9*3*Fn zo&^_wfcYqSuimu#Fw=XDnhwlqBE$mgM8CCSnfolx1R}#u6N$XIIttm0;N9%JSkiWb zrdy+#S2br7-DXHtk2sgUTwU`A7mnjc+hT|-6jstOj-_bc7R`V;qj51!zp`H?3Wan0 z1Zw19D?5(h-JS)mvPf3BWbG$L5Ybxwkg9F`0<#?i(*Uen&1ySbysWL<3b?%s^>e!o zd}0WG;bd(Wjw_IeLrs(7x}?JsOC-~wctnVaI`r5(ny&~F7OF#WJakh_LdkX!?!8t-@tzD1 z+%6&a#fpKdGwdHzr$&nr;P*J;)40916;OwCjg=i5N+y=t`=Uk3fzENGPwt)?(PpeS z0A7v~Z%?+4Ahj4mTtN@_?1?iFVTocr^FJi8 zXS7=%?%0V$sl}u}03RW1CGsF^;L9D$%F^SOO>g6qSU?TsC^ruJ9vuhO7hGz& zGD%^8Ib82tTE@s%zGo{pg%|4Y9SU$KbccJ&rZn+zCFI1ZxQCyep$yo4=o}0yGOo?I zZO$1O!_Pi)qU3=`mmc4RYSc;pKp z2AbZ?Btzxpo8DS2QY;VNqC{boVP-a!jls56oN0=BuUSXOqX;iXWa0!dHr!6>Z;!HHm3?RY~yqXHED|B7pi2YmMw|O)bo5EuHLaW#R??jTutpnMWjr zrI-SV+7opxWKkMRDgt+Pza2@iP)fW63Jp~ZfUpi2GIM{&d6kN}*+}8~>wUY3&xsAg zmCmp@*qkw1QC+Z?3Sl;cE#7b!XP!I#vcbdC4*y(VF*H!CVYB<=vS*rT*mT~v8s4sH z^pTH~hP;pC7f|@3EQezf@nS{ZS#J}l?DQ9Uof>~Z zKAF=LWBZ9Z5-rO!36rA+swJXj>fdA;*ekjJr77kD3>D$qQ>(-uEgAgGtwR3?O&J?F z8_?O9*t*c#o7;CS%E|T7qX67=*<4##*gy&u8ijCa?zLjHG@Qe+ z$C8DQ#D7EHH0Bj8Vs&{pjH!Lprwvue5Ze`c&h_$~llF zand$q51)30^OSQv-{`i{EIH@M2A2=OKxoA%_i}{4%}-sQ-VAsZ8xQ%U9x-P`DShG{ zECm@Ibjf4wXijj7R?{q>R#+@}{9dK_RpHOsYZ%5|wbZ-M>L3v(Q7h^6m{#N)yfo@u7Furkc7Cb1Y@N-0k`Ys3bmyC6F2 z>D}@UJ6tvUE?<&@py2SZGU=bzH1D8$7ptMqN2b|Y%cn)y$v^!I6R`95Bum{R?7zM^ zez*$$H@1U+H{(CrT+K!2?&D`s!w<3QKiXW_#MQ*w&fdiF-)!DcM>j&gbGUanCN&{L zL;KU0qe4DHAu~M@B_Tm0H91NrPE!vik%m@ANsNXv0OC)QbAK-xdFSN#H}OqVvr`*N z6H4xxnfo&DrP1oJVd~24Gb#6C3k+%L_)v06f|^#wpB5+&@!MQ908R7O^@+!pK4VIu_mQIq^Dvj$67c{x}}; zJWozTpj$}s9x2IDQHk7T=-wm-g@cHsA)VL_0P#Pa?Z3H`f4fKw`QIaIeiXLy^Zco_ z|8Lqgb+I*awy?APU;5gcTb|ofoKdv@FCBR<@tuxd*~n=vbK7jbH2PdOlw;>!Wx^;I zOb@1Hl;#(tt7fKYCuZcK=V>NrXe(9~7r#f-f}~q9DL5Du7H4Oa}oHDJLlr@qnU;ASuT>OK5Lv zr+~s!%}^aY&6dxje0GVE6-#JTzQ&fGq8B4>FBxA=kop7CmMFQgV z2ynF+_Vj4+6fH3nA(HTYnqc*d?rDo^G%?yJTi-1S{3Q3C9X`kF>N>k}YdY2ksBi)% z%^1aAHBf^#541nJ&%bp5!VyYxm{)*^bf7aiSRY7|Ngn7Z4?*8Rm3s!4?jV1QM>YLr z{3PIuE}z(`(EsgIc?P@jO)jb)$lu0e4f!Km(C;*USqjt|nuLjddvmy$$61$s!v>dOim9olI7j<+Rm#?$&$e>F^? zZVKav5Q=VGf=%en&6W``p=IIi`TohGo z9t}K>GkRgVz{mwOP6>MM?CfT@m|k>(Zv@R;=JB|h-DR8-6Ady1rKVK7KjpPN81s8l z$L7b)BloMlX`=d2{_>q`YVc2;BU#deCsmtIhqw*7Fh9rmwYuBR`)FN2y1Q9d$4a1 z=NTbVij!+MH(``*ZZBMQ&!{$6P-T@OkvI2DBU#>dYpyv^_X^^5Pa~yt!8Wz3c0H?W zy?56;i0BbCOwvY zK<<<`d`AG=?FbjSAz*)70T~q632+;|a0kjK6r|27z>tbWqy(hD0m!37$OaoZAVdre zWAo?Gfr(8M(l5(&BrLiuKeeFi*!p94z2eX@TGznAA8aY(KfLKPA{cYS9Nu~&GmA_X ze&_k+NEK1M8C;Q?wPtlf9m}C37Ye1dBCz}o<}Q$O9Kp`KNGv%pUiQbidzIh!!AR8b zWtP}Q!AnF7(L6D*g@fL=qV6vlrG*(I?GV(jrvhd=vG{W<{`eNDy0PHvSE9S!3+)Rv zgmDT)rHeAw;YMZWCx6Y8hf-St^u;^2jh^3iz%jEeHrt!uJ#ougptl)?vH}KRO}hvK zygY;h3wCXnhVVw80Ou~adXwzHV01}oc^+30F;X%JYS0~XheU_GN& zZcuO0Q9QD}_KSj3p{c~hSb5kKoE2T_r z?D;x#X!qVimc8CpM(}&D^X-nz<~0ujIrBGDjqzF)$z_FGz}X(@SP)K_47i7(wo2!z zeHJsFER*+nZW%4Nn%DY#;TnKc3$-YqTu2BveE=2dlD%8GBHU9=N6} z2re$`WpJ1E^GS9tKMdJgsIg6arjUJa(qKZH~M0W>R z*APoSQ^s2!fy-!TPggXG#Dv9S9~6vXzxg`tpj?0m3+8&ptYN3DY>dIPP{QdSEC?{+%8b5*pmFhh1ksFMK zVQV&xAGTdoRX*A^{K1Yd5b!7;hpn9hug%W|EB$>dOswd7e1 zvb|lRoX(joX-A9~8cEpBDw}@>PxwGX4(>OEOEW?`wHKAJ>pm}mh1%>efm>h7bGWCE zy}VQ{r)#e>&;1lSfYX=;-XWSUonG32&Npz1LOsRE)C8{!;!u&yla$vle3KFlUXp#_ zjWQDhLUHoud_I*aMbcSUZYNb9)|I6h9TN!?#{ZCpiaZJ=XrrE(4%Mr#4`IyLzj25L z2L8hCxRbZ=akaSJ%0@HGnL{#GyY@d2T_Qk3x&uYxQK(IwF-?N#7n-dc31QjFSp>1;)D9=^P+rF{JT^$;<33x4B_LcC3auge7WfTh$9 zBQPTg(@=$tj)Q&OCebU_?v}$4#X-;Xl|r=g)6!%Ips~sj0i;OTqm3L568C2j(O)pW zL9{a}`6xOz#AmdHbGao{7Z8uNYTdX8^3sLm`2gDWY$IP-EmsHXLfiJ|1S%-oIu_}t z=B&Hr?R+#2omj7Xd_-|s#TH8Mgc(eh-FrRui485NOAyx0_M zcJuYVWl#n^B~k;8xvb}^q5aO+{S?vaT#D$^A6MueRm*<(`_DxXHt@jFpU&=|cpU(M z;UD61dly4%3nRLp$kqAhY7v;3HhM5P>ptBdLe#bzLHLmq1>YQ9wLsA_7v$GQvop5xQg@ zurui9QFxvw?66Cop7gdJIn_?Sn=W5vL^FaX2SRB3r-10J`j!772&FOt}VMTK&UZE z4@!5~Td`8x9e$;f$%;d@OM}-!zY_T|I*1WHBO1RNPO4Go5s?7BQR`FFir-KhE<={q zA>24Uh=c~A3T*uq(VO)z1_hJ}J=li|o(c^EXP#X0#+wt|ArP)H!1M(|gQmdEbWAK` zcQrH+A{%&Jl<3yZmZqChc=Ym&9q2om;}JWXK0Q^EFwH>^lokKg8Ou?%g@=cO2SeGV zR;6}9GCF;TM7E4`45C&knF%^Bl*py{yJ*(=NOX37sR1X+RE-!xwNoKII?owuk zs@k?2f`d_tevLp%Aj%3kC+=8@*>-IN{lpojsuVny2{>wQfum6iUbYgk;AxqhY> zC0&k}6Zx0;#A0828d2A2DSPp<{_k$R>6VC66KW?nuu2&YtSU&D_$8f8cLK((&T?RI zl;-`ND)bPC6ioC2Ot#6?5~g9DVI3?eG=zkqA6B41(b{No#HJ=e_|h0bnk`MdHl`O; zFVy8)ZZkT$c)&InMj%W+Y@g1)8~1Q|AYm+^q;3EN?Z`aOqSp3SIQbh-%V3fO_)$Vz z(`WvH-SRhoaByr3R3#*<`N3l6o3wLB4zo>xc%O-7oOHa4N3Erp`UH!Fp%c^(&k&26{qK(shj>Os4^r;WSYcm1>ZM{k0wt|Z z1;o~iy+}u~uycLoWc^`Q>K43A!ri9LE;6l6EuhZrp=UsQzR$`((3cVbu!`9T*uq_K z!U2Uz!CM~Z`wPFpjn5Y8v0ra2jl(Vd(pIav4NO>AKISM2_Ma|zklLQdSz`K0i^-{F zHo=%E4I1Y}qJl_jw0;;kN^+kl$RO)E?82i^lO4o%#XB!N4Clh0ZiS-qRq5A{IfL{; z?Ht9go1>bnf>s|^jQ7`+SH){mo#s1Aenm&tsNk4~CY~oodfy;OHCb)=$idnZD0h+w zw;-!6?#30=D=D~ZY_ zDRS(5D%0`HkED9H8G>$)_q!#U+yehzQo)oF3g2t@qFHGQ-zXs))9P2$Ar<(G@Sexb z5J-?&gbEv=zAiPJYtY;+1mH$+`hAI(iIkIr)Y$kEjs&3Zs`B^@F<6r|r%TchVRaD6 zsu@U7%L+$yt2hy6CVr#zCuzU*(H5wEHgPkx0g~OreTD;jzS`}IvPr1G)zG1$Y{l{} zT?U{cMWZ=#sHHLe(%Gayn`Uhs_Tz6!g2e+1&0V(59(Q#)}JidrQC%(ZDYcA`( zS}Z+b_*U)VaSMjPj{@lL(hGu`cS5VC7c}lJE(= z(XF#9spI()fS-}dq;IHXr7{s(VaMfPJ^%yJj>mx^Y+4nqj+r}I0b5R9;Llur-^~s- zG;!7C<`OQuQ{k-p=Cq1kC8LVCwx#ecn?I-%t}=p)_F*}%>EL3BDu_BO4K8J4_G(_o zF(bqBT$tze1tDXNF&N1z4*72T>JQ5}cg|6djdJ6rYtFZ$5me~13%W@TO&f>?e|xTb z$k(nQAMV|&N1N$LvRf%hR;|r-PyWZDE{#@|ESKRXSUXej?$BJiRIywv%;2h zY{y~wRHu#uX{3grN$2AIQkI?T^Mf;AShID73fZP(O>dU|!?AXYe4W!b%hgq0eKruY z{mhDTy<<{y9mBefwUeW3oguR9(dDBJ!le(ZT^yGP7mqa%_5@u2$Bi6I2RCdFM1W2x zzO(a=*i^+q756LqoI&(*WLw2PT1;bCk1$AiA_MQ+pdW^4g{Y=hd1{U}%i?p?x4)cM zb^UDxgMWRx4=V4^c}2j&iC+kxJ5cqCT!G&$@fr$W#MfYw`~HXW7a+C)hK|l-$8s0d zaTimWd+sGyOZ3%BqYu@n5N2&iWB>&z{N+mhqaQ&I8mA=|cAz#kjMDb&m5$xoHv_n$UeVtyU>@arwsn=B7kaJlb9 z#q`nHS~zZ`o-uKzFKKW=&4uV(yc6qehMhLWDRLT;5}I4{GGC(@U7~Eq@QvXo;X_Ft z*Ec%XS-@1w_@JoD7nU{jN4Ah()5AF50S13$HC5)8&We>zp%r(m(6VHZ^d{HdD_t4# zzRXL7q|fNx%LSL}b*fXMk|(}7iJE`@6%mJn_<^DAB6T@Wx*h7({`M$PM#g;-u%;$O zjjbY*S)gOgjePO3a74<{D`@2s!f?ENO&J`tz}KVbhT~oD zahcoN@QzHeklY7-BNcE+?d__uxj-67>i#k`>!8bHEs3P0EaDSlgB6w`#-yIionFV- z->sK4)Kxe+`}?(4&-kJ(w(ISid5bx35@x?ny@a%y!HSb)w@e|mLS1b<&DNJ_1Pr&z z{|08V4OX~bY!DvMAqg10w%C4uUVVq4L~xr27p8DV+hl#Ek;cJYqG`#g(U)1uMG$L+ zenFso>Ufx4=j#Cy|Ja_j(dhi-$R^A65(>&8vP3iGqh)9232>Kd-PQKXj%QQ7ck+^f zBg><$$Ip+ADo0hulGPEkQV~-;0VZ0`t+kyOqH ze}yJxbmCw~`rhRe9}F#B?i1dYuCHgA?_Rdsi>lErf`aO#nI3CR9!d-&t0d7XyiQp< z>twY0E`I2ikDZ%v(?YAE3(Ga1HIDZIuFinw&s}ACn)j~zwZeBNw$xis$H&F|@GpxY z>K!GU5I!*~YHqGV1+P2@u}MGJXXBlVqF0oVut9r3{PB%{?oB#msmF#v4ot>wRd{9y?u|)1)Vsr4% zmChSNCsX^gLC~kicS~$8O%DU6L7XWUOzeUq|2-UOq>j;MjZKCArP1RSbQ9Is@pXfJ zeQU!=zC~n8yVGZftd*5Uk=f*RaSAsEE^w9OU@C7SR^W2wjs*z@7S38XW@5EAf#K1i zuV$m*9wwndqz%I43rs}ZqxokQSCnd>oO%mLE$jK31gXLr4v9STsZ2~wb(zPpI(apcG)6vScW?^dlUBnjHq!vJ_i z6RvLo%Nv%W9%x?pM9?c_yDf$g3cd;olog)`E5_9`=q=3>O{2#l&uCi{p<8z;j%6;EC=ggdmJNF!6XJo}u zyfWR}{y~hfP^UOz340Y{$o8PvL zN@h3jUSFjszxfgn3rVJSre|CJn0bgr!%PIYieV3>)WU>#3nD%RCCY>F?Xh-~C`a!=U>jKW&61uGwKVS)PMHI%n9D zl>twI*p5nsUpU~ABQ@|eJVcrbcpQve7~@<8-E($H<#<*7w{qvQcon*hyJJZbX`|JB z`IlR-(-i45gi#L%qhJ(~)1$@h-aB}nVE$?oVGyktKtxc zUPzhlo!>1teoj&5PccKmv@+Onb6g2%@AxFW&hZpSl9Bk_28k(2vB>&S&E3w29-b0i zKH?8)PUpw1Z;BnyL&Hd7Oll#rcC1*Xai!Vnj9%!QK8oVIvHl!i6ZLf5nZGOfAc30l zLskRnjFSDWl_Byl4>1&i5tHQ$n@tCo;@vT%r=Mk1$O#V-YEI=o4$U;bfA7R#vu>J$ z6b9an(qAH?b1+~?lQ}Xp6QFZUQFig1@ihqg+g9OyMNao&I+g=&&mQixu|7lJn^57I z0(oCt2&jxFay=mwi|&QHiLSZGX0+*+yk8QMlMM(=Z+gRb=u5zyB4!#5F+QQ$H{~#+ z&<-|wrKCS;b*XntNnpMR9f}w3Phzg@ai3EXpqls5Q-~GONKowA7F~dI07ovK?yfVD zlXH#j;yrV6z0T~JAtKC^iV-Hg#VYExFO{zfKGa$mTmhw{@x_?#NF}5dvBF?SiOU8H z>C|&Q2wGYEd{LpKVl4MMC*eHRSl9NyzG$}JpFi~DSbJAk1aI7}I z)+s{b>GIOTpE{%lA+RrpDrT@*?Dl0^t)pia@H8v>4KE%Y^^uKtX+;)y4oDM8fT_7K zK3S^%@ViX2EDn3>?4D=+gAFs1#-@WH=QmPp(aX;xhUDn?INK_u>%{>(!ZaB0D2@uc z2*X{@B?ax3@|o#VYAgbxHc{0|MAzL~IpDOxd0`tS?rXwddP+VtnsHK087M5;9ZUu# zYRGw}67}3f3)fjq&sE#EfhDSbYHp94o1+wS-@TC_6yZ-651DrfT{JA!q)zyP_GL%< z`Qq#GG;$CV>89RYQtK9ADhLXqGdxMpB3j=DR+-zKx9x2#nU9w}D_ku~8Wh7Sp@#jY z6L2W3R(*tSclHcp~5a6CUJJ?XfWIlpKmp0X0k3r%kI*NHzRm@j~0ljSV#GhgNt zW>2&nXXEYPID9+lt8^P_g9evNjD^bT*Z1dQY`NV{D9XO8sJ&xOlg>RP$RUDnMMV9?g&2 zvkNZp*FcC@+K~!Mlt~R*xEJOh2TZ z%au*OjY&TxP$di}7jX1vOhFwdX=GDRq|=D>StNxe52~VzK5K<&pL?9J*l#goAs9_$ zZUxhrmgr#@JZ2RJ-43>_qhu~WA=4-isz21ncz*P0%2fD}p07og1Lb|kNmA#cdY#oo z_q5TT9zVen@d``0@=@1K{Z~q^(ChZ)bSb~ zv(5x9Ow}1t)`x>)H@Zy{g|QTt4o`U0pd?Yyy2R>qJ41;^NGL5PpTHaIAO%ev%d<3^{>8iW1STSIE}Q-z$V) zvbmFmLK}pL9ZOcP-YYwmiO8h&(yp&?N?yGWVV($`vMM^NR-3+MGZGa{(;mjfB~yid zl3akXd`+4E6zj~VP?4-R#fRx;A2U!w0?zvrR>EV?-WJC$Zg+TuDnY(a$U8QSXk3r*#AsS+bCX)UHy7XNwA;XccKu!4gYNu0I+@2{m>P z-opHdKft(r!PX{8edoPEx3F1an3l3{lL1!Ypp_JS zkkPGoT5xX!N*WAg#U*MKnVb1pVHI#pND)aiYtON6+J#fmmWgCX!m2fCP6y-BRDy7) zsi+1O%YdHiH-W%GzfdYUmX*=7NbaIWWX_tys};tgiYdeNVnmqqoC?WvVtDYSOg zo^JZM>sPs7qcEvvz_9gY6dR(l?Z19C1o<8Vziqw>27)+X4JFl9^KVn}#C-ZY7U-vxPzF0>G} z0{=zs1NUJE&Epd^dMQ>jC36faQYkQp&>fC2WsK%%4^$7?A|jR>Bl|BeNmT1JWYc7q zQ3F?qa4N$qn0D98K1~r1>tPs-={E z;yWtfv#ls<;YTp^rrJ9DIDSo9p&JIwXS}NC102p3DzgO@?5b@SltJ6 zQP<({cR^ByKuVwP?1kHlk)qhc_~>bB!@$7P$4xW@a|a7c#18Dm9ky>RzbQX~{sE9{ zPqIcX89ks^L~_t*z;`l5I>;bdXkpOsyJ2OfEg!wZFzv?ZRThA=QO1TdFkNX`!@GQSd*Cc|vFzni+wO+) zIDH7_d|ZG^C(4b@IzB(->5@K;<0}neDcPggZoZDI7Zb!3_!v%#d_L94Hp6n9?Hc;$+vtVr3Q&{inln=a2>mMn3teI245)#L@4&RB? z(cKEw+DLB$yny%^T*lqDO8nN6i0(PGC!6*#Vla@}CT5}AY}UTCJcp8yDldUZ8NF^8 zE4(w!fTz8|z!iNcUqDS&Tw?f{8nk`A+haNR8a~z-0>B!vcF}!3R$}p~gG11O%Lfjf zP^+@}{4~4RSVyTTX=k_?aVlxlF0LmqQE}z&)Y#h8zFcaRPr(H~Z?lp1$=N4sHfp~z zFIy4$^dzK>VYYVH=XgZ6;8hdlv&BHNE8Os9WjnE`R#Ql3vBg`3lgR7am32{m`dT>? zAPWI%jnk&F*6xN4;SJ&?%8T>;9eRI^(lh;6R+^sED#FHOO3*KJ--5r|pV zxCns|=y>x0H*eVBuBBgQ~-3oB+pKn|G1~)-RmNMN6 zG1Q_n;+$gB8ff%qltmUS2b5+C| z?1>YXKjR2k?71PPIxS9qY_hul*MZ9K`*hO4YJv#E)qdCNYR1?H=R< zP{TZ@?yAIf`7%v87(;^DA4wyP7-`}WnQf;#ZpwW=0#8tf#^)g_WMBiPP+|6EDIi1EEy?I*VNFi)@Dm+p`L)ZBTCL(tv54OJ zO@QtuB$ZO)zgB7)Nw*`C&SBn3T$@f%hf@U(cv;BAVwX??ucXskXKHqZ=9wNWn`>>{ zVmS|vS*ot7lY^VJua$2D-k>dyz%W1U;lEfZ-R`x>;iB9M8NL1FST#J$CBSV_CA^Jz zobsHtYskxHR~YMQxpqK%kx-rNdlWPMCy#{aSqi%=(UMX|P8ox}4D7;I-`9Q;9;jMD zNk>6NCLfJS%u8V6g*}i;L^fdviKaj-B}Bg~WKb2*)yY$b!oA@PLn9XcxY%sHxAks4 z1?(lGynR=TmpOr^HO7~vQBzWZ9FuZdDOR7@LD(l#c}SMDPX6R6SV&TKc)fj$3ES`* ze0a^{_#FJvqVaBe! zD*o!t@dhFel40uRjuwv<6XxmxP*V3cT&#Al{VW*T_NFWqrmS1ka@ZT6e%?Wo264}c zXf!H9{45GB7H`x!=MkL6&!F^lnysKsp6O+md?J~KXdWLUp%M|m+ivcH5s;tH>YbE- zZxN%wTXhZ5y3nV^sLHz%W|b(Kfm|vihZS(PMgTNPxqrJWUuL1)K?#kAdZ*lRnGG)+ z9=}0yhKY-ZkprUXmT1-r8v|>oXRx^3)8}1zgwM7ZAyNcZ?v9l76a!XNdiOXxG?~c& z{?rMRt3vr%R)|E5{3SJfsq?a*!Zl1FI3ad~nGBf&4QlRSKs}hOxs?t+PC6;{@CTJQ zoqmedUO1H3{y701X6V=~c|*vDu$Jde)USo(dlh+-kjv%U{mlE(n1iJ9LCtf4Rhr?; z-*Qa1vP76=RN)O@tr-BbyGr+@?)q>0IgXu|C^2H(^r|;&fx^Ch9Wxl4$=y>9UK_6- z3Gu?KRo?Ma@oS%}aPUt1Zq_rHWxZso;0)4yh(IM%jZYz2z_hUmx^;s=kTO8ybaHUa zw6>hyez#*C9J4cr=plE9t8YSHmjh;W(&Cp{Lv%-DbkI=F8vc@Ua3;E*X{eBJic%*P zB{LsJY5CflS}*4M*!Z|uF$pzDE>U7SC*R>+1ZSRnmW@zDciV!yvbWbhvZMiyxqZJ+LxdqxDGeKB+ns)W2h4z~ z0?qI?RbeXgTjF#)&|-wLHdQh;7s;9B&eA*oFzIq%-yHY52!uD3MR2&n0je^9tZL#) zeZ`xEO#TLBl>V0dUwl_b>luM}Rti2+Zldu$jp93oVOsI{%r{cRFHPtbqqB@&(v2{2 zs_#)G*zxiWw!L!bAZvesO zoJB4Odlyu7w3umu>aVUmxHyRTk2QGWjP` zHpul3N!E%1j(9E~mwK$EFg|8S%gGUX`53A)a*`qC7*`EpnSPw&}UxF?BGRVnL_WW5-)qrq^f5tZXlabdWsc}-7 zx}9y{Px|vKo=KGY%wCNzW+sM|szS(zmD87QP>xwDa<{>Bc`uV|sxs2OY^`N6X)F__ zD&(3@TY=t)ob@1`dc1FsRk%{aDAzeuQ>E$Ji2g)@@DS!D?Q=2F(#njW(vpfcQN9in zx5w?DOe(4#KLxQJv+VPI{!v4zPpo1DXgLE)1#`G&7`jI)9{DWF&2zT+O@pb*2Vx1^5A^EW(e=8jd)M~%A3htwdy2{G`(=lt zpiYNrCk{r#hzwmtVup5TZ4IoP6!W-S+NGJ-Ek)t^vLilsiGR27*P5awm_ zKoaOsmyfONO@4^Yex+;)NVz8R_gecBO6%;*}Aj8s$PtQ)TZExxV#g3@)SuRAjzc*uqsL zk;sC$m`7w!^0fxwN4ceynvTX+E5XpnaCIRUOSEX_(M;BkC0@^2^k{5l&tr~3M04>- zvTRDh1q5_56N%5@UB_-~+AF(zpWLm1_M~K09xV`!utaj&S9d8e8w+G&+b)apaj6^L zHmP)VP7ommoiTDxK1=Egeqj= z%3iWa>4~z1Hr&T8_TU~Vm{O#SxanhfTPf&k7xOMOLPil5q;xMF2#8blrK-=Wtz9EL zx9hi2tRPw5UNC`49Q?XNU^$x6uvl+Aqu<^*XJLl1K^Ijpg^~vASu5swl{p zeyR|fu=dQU_*u&5r^9q6x!wNGtE*F=X)ofVp)RHLUQHU`ovy)6W_lxokqqVPD%ds5 zG}<@Y@0-y~^nRk2Ft&?$qUu7;MFnQHRtJT%i9pwv)QUmmI*>;XwX)!j*M<Hc_Ral6o>96Q5*LLH>CI$BpaiCagEpw&2Ymv^7(toV=Z`<^5x zvB_l^XJ%S=QH{lsC*Zns>9^0cstbApo({=W=3P9t*-6Fnw#!5uMyc5k=UHGx?86k; zjNMuhh|E@LM#B>#HqSLiSI?6SubQJ-q>>!bI<%~To>IG)OVyO>01NzzI6r0!9c}@IjQ`1(>BR9J04Z@R&`YntMf}=Qz#TE*xCURMli|>c|P=fl!DvPK0R939mOo zEhL#h+aU|gqYxjB`mG#YGTEN-Or$|Ag%WTuY+s*lXYCwz<-G|9KNUIO752R(c^S5@ zFoZ520UeG~mUF~MNaQudm&J>?+&l^W-rWhDMPiAMvc8dQ$^;o+3Zq`0A}|aOhJ&n- z8b$w9F#R;U;zXq6$n>l=hFrkyhJyG-dr~DkTV!_DUP-|=c=v^9Rnt;mWU%1OZZQeW zRW%NSBK%M;R-F3XvCz(?F5F_r38oEZ5?8(5;Vn`%UKD++FoZ#-=4eH8z!e5U=mNGj zaQhj)ZmX9K;)m6vOPsxpaRJjyPG`TzVXSDbAP^wMvg#9=n3S|yZyUp!xU|_&Ru%=S zyfGi+lx3M1d?8$6bjnoUCEnN3NhIdSf|WWT}<}4=u)XY63i~m^oVKyYzr)= z#w3%Qt*R^X<&#x_#x_oDY3BP6zJ7($ zyY*RnslinMnPQ7a-H5c;HSsz#?)r#^fLBJ2$$YTWVSsvkDR-@2B zRx3*OK-h{yJgkrx@IJG<9H>j|IoZNU2VL>4zS(dTz0)E~v_CbDeL9rjj2@qU z@~r!mPz5uPIF7{Yl{l6^vPF^d z)uRK4iTkuHP?rxgY!U8~J#Ch|tbS>`B7qjo&tK3uHT#jCn>L9>VB?ulbj1Hdrk~vJ`u6tH3#LUL@NEncIu%*6Qd@3}@S)`4=PG zK8sE-()Gc0%;14K-&AeU=j2#Pz9Vn8^Bi=5E~RTd4w|QZlmk=oLQUJLi{*$dJ)({y zPwN2Uyy_2iDKYsrOZof1uf7fKBTdhjTGrnwl5*suX^T7 z(Mn{Cj}+-p1k`TZq-#6I5NU&*7lOo}Xl|adEo4WvbwC$E(k%`RY!qDRZqAz3j~H}c zwYc$4c9BMVHb|5B%pK~5q0c|b&zO5>8c z*DSM;%_8-rs_x}0GP-p8@%^z1(yTfM&se<7kkGk1N7!%NefX-U*ww=s zYF*xC^6+pW5!wNLCt^}tIVl^Xk&hY~m2LWEsHee%WpOocoGLc)3FRqs0L26rYy85{ z+Nr9B0aMa8spd)mID%6^faVD=kd90b$Vl%SQerHSmb`S5J30=L+ww;@Yg^L;_|)P4 zvV)Y*nL3lMX+pyHUt$YenZXqqMbJ}_POT6|r9IOr!3?1}}?HJnNup?*e&A!UmWHQ$bp7JI=PhVjEL9s4gXFn_%sF*KLPqw@8%f zezBo}DUux|oeAVT7E6uvVjQ*lG;BK|34wojaKI5$df)p}<@0OZo+)bl1E}L}SQkOA zv^~8s$jfK;t#CX_+xkSpf_DA9301U~@K$$37`9CyjgwMpR7fsxz}dES%Qcz3mp_MvIF8m;8yVvJx(>SaNsO8m$0PxtxoQT5gVe{bbz-|BA1ots#KWy!k>MhHpryU z*7G6g$`~nQb8w%%n`AUMNIXi)8H?dJK8etL^Bx1SM*lQdZp##R;>nXSzny5@vn>L5 z3Ib2H3VOcg@Qdb)>DMt*YBN?)jAI&aIF!A1VH(jooW?cP$K`+k z-eXoU)*Z^KbRv)v!^iH?Y)otVVsvqAM_3RWza-Y+f`JV1;p*qT6{nb(H1Fzgu?%4g zw~LCRVybf1%b1gq#TA%4qz)o}xL%)lbVxIKhFiItQQWf)U*vGp8TwOf95Qu<-7)Ag zv}}yd0MCXmpb2w9;IhWg>H;S$_o;($2y08kZ}$@Mh|O;WYr>t$U$a`;WCV7XqyyiC+|r*FCeCgju9t;T!m``Hb={+YRN!Xf7X>L9C~5=} zflWI+YOn?&kv*Ru({0sb zfV~{or1n){xmHpF1%A(pa4Ryf1lOor#ws19d%y)+W+Dk5$j#eL37S~}vd z6*=)WG{8FPL)u5dmgui06Q8J*$QF9{*)jW-Wyzthe+DI{3IYAJwc~P6AC(}y}!bCls-!e~tc}{;WmahUa&WT%yCTo`` zl~mSgavt+1Re0*A{5FRb@OONyX~~F5KhlLz^%pN^QXOALX5-u{MiL-s%Ggsd>l+d1 z$lB$tc5J+nGwh1}SR_|1J5gL9gwN>2(_~y-3Rj1UUa9Z~KUx}?wK^3X{nh1%fr$}r zL04GjGF{B~F_>Kr*h;Rt9CJoTl~EVU%n|PW-d#AT&vfJ66F_pW#T0VoKf9E2rO>so z<%k|pEbO!>8SVIvxP75Rqe)Yt!TzOp5sYLBw#j0mC{bzXe0EN?G;IYBuXw5drsTeu6n+ zR2DQI>-ToT9t(GDbu39+E=kE0ZItKSMCkRoGbjxyE`~Hflhmjx0=lUmqufn3!06CK zrrtnoTX`?u2!$5%@DM*4_Y(ykD|M$8gDDG=!ZD}W2|9e8li;1&P_z>_C}d~m@2yaQ z%!sjMx*R_?POr}C;jlRv1=w0%Zev-)h*tlsLb;Xq?&x){X25Fyv>AIBwN)kj91Hc!ma3cJO0*d_lE9K40TX>}`k48h|}C3Gb&nsFyxg@HdxO z@#=o$u(v|z(SJxS$3@M+H9;kOVu&7%aK5N-=pNIjj!xjo+j{8J~?;NOReT&M(wSq5csTW5N zDn(|P8F{m=8vXqlIw5&sIxadLvGJ_H$wE4Vv$ZV7u>CerGqf}IaOhH?nd%(xB1eKc zmU#+AYJwVebWK%(qTa526cQcOcdDSd>KiC+wn#4H;TyXIO4+IbCk~6G`sM0aL?Ao zrLy$bBc>PwlONu-tZKKnwA$&pukE=m`=0mb$1lIH8M$uIgMZ7bez(gk3-l6pC?*Cm z;6#(R!*KETZkxL7L{!l725JZivY%{0&Ns~l#X@IQybKHn8mz_$ zO+QI~KEG>Lwkg%`8}dw&j%ec?ZW3)Jx6#YtnJvs1$$R?#%|1|kU_P8s&s@>47*x+4 z73`()_a-+;hVJetCfH_l^%)WrnemHyAN4x*R^-G`HnQv7^jMRN%~L|%V;(n;L-z@S z$s73#UzCKxt;lKdydTl+g5f;1lvVmEo=v1nADVAm(z@0XDYP6unh6`O3D8k} z(u(lO)$OH1F;uky#}NY@f4AYC%)5w)Ng%y(Q7v)~J7ld-rI#p(Bu6Eaq!43p*drRc z>D%q-gjKselEudcs?Cj=8_x3v zA>JRRMZ((P2cE`LjXg`lbWt^X(8MmVV9KHR!s^l{M<;(rQ$odbYV5KIRnlin4jC z108$Rkwww(pu#Jnk%CJMS5VFl^Oc6jdXs80Y>;}#;d7>RR8g|PV@aNPPjBZ_2G5|q z7CPDrtcAp#*yTp6X52`gf$eFYH)q{ATnQvq?y_~md41@QdPmONkEWQLLx4A?MJOvp z);&beY|&;`B!(JR6x{`+IUD+xdr`#%U`mYkpY25q4^M+jcLu=fNu&4 z!8;Hc8br|h5NA~#j)h#n^BiLNH=yc#o-as( zCI_8TF$KEDH5IthE7ON;IMs8dguU71xtRWxw#+JywFQAzH88J7?j$Y$W8#GLN|H^0=)pbVXNh8ltfud1|_;Ss#r`1)k z`-WEObo34%9m4@%62702`Div1fF!bS=>Wb&y|;Fl>nfP%vSDS~ZY84^b1c+_FQzIi zag2tbqPeDakR4G?V1G;NfPWh~HiLhy6nJ;I9tXch{YC}*_oMwj*2m=-YaD$=xnCY^VmASVyNkykHpUq3I?Csd7?OCi=bI0 zyYz|mkVv)pLG^N<2^J>YjSqQ@(kPVaublEbFxaKmO@`COsYNk<=%^A+9UleJBh>-E zNo<&@JKBe)mn7rNvc0yVSTHDml<7l1K~*LTvd9e=is0u?MPY-(F3$`e!eCq83xWIs zyd(e&g>d2ROX?G+2E*~PHkLBjW@mL2GC(NvBtPzBKJZLX3k*zJGbT(bk?L$Nb4_>e)CpZWu`TSA(U%r)-T7TQVpz+(S-U9i#3YBw(j zL*gOFECb62ltEv+wlD%du*~`EmacdNafxX@Q5~kR$2ydI5g#4-%7}+)8k?T@mOL+E zjkP)Plr(`nbs$!{d36wK6eko0ba~x$?ptkDWU77vAykXu35Q4Xl=C`xn9M~Y4mj^Z z+wcwhzK-i~$|N>w9ow0p2WBU79JW%}C?{n0+TAWL4fmPbG^a=z;$TS^)|DNtO;va@ z6fbou{~DkB+p7cMD@F%4Tq)>7cr!WVYnW)tR@}~@5I-DSM=jt^ea0xAZdMinGb%DP zK`9tnGltGSQ2U>i6bJ}-vdG&=;m=H*LljZ|( z`4|6>B{n=72&0VKx?`fbQawwDmNZ#un-aKhMl@%r(q zop$XruqPA=j*d+@Kma-m5ozqG$|X<2E#_Ep+%1kztdW zFJGxXCCuO>xO(nO6-c{Ya`JbQFV5QL#8uRBw)+$0P=Ggd7EE8L%R0u<&Ov{|=FQ0N z^s+>(x=H)Q-2m0d#RGMGU|z8H5h;0qn_ms6eR|ZoK-dCk883I!Z{a9WDP*~#kUXk* z78j^Fk!;^Lj+NvKnZXwtKo2k>pE5NQP6jzW34Yc?XH&pirw)4qnc%gV$c^OIiO%8B zXn!4l)rPZtPSMugaFqM1ODFhdLB5;HRxd>vHA)=6OV`-$y$e=-kU2}y)vS9@%O_dw zXJVLCs&yc+g~m}i_(8?5H3h&ZRt zI+!9Is`nYwc3GCdgr6DG$W~F~3szrKe|ROo7dW-84A|h=KBuZ*=BKXex`hd`li=a3 zpEtp3*wB=dhjos*OObF(i|)K=EC&%^+)r9we}C2V2xKv7oHU$_MeoLDZWM2PE90jm6`oG=A<5@MiAYlF%@-CTKCP6+^@ADJO)+PWC5-QO*%_FmVrN=8GO)aBu zm34h0U)KrQ-qoF1W!cJzNi?mE7NL4hALdtzDb5NZPL^n<#i+~c4sVD$>H5M)t2z9x zp6qxYy`G)9XAR#TG9Kvsw)I26Y@{UR?Lcsi{h5XL+3;X~)756bk-Bpnl-sUHqXxT* zLFJrhb4!b{`eOOk&Hbl;r-qMqlVaRI>(cQ(0bUUZan(e5K^wmlV{&x^dqF)MKh|bg z$QbB)r|>y#Pm$e?J>VuW9q+PeVSSEue?x?HQf*bUFWCtwo1oq=5B9w^Nt^eF#0k8# zdfR+nvS-pLrO7o>-thXv@>m_Nq|<*$;FB{ze`llA5? z?FD{@vQ)~3tH8|A=ww9v$^HHVpSaWBe&Z%(uvnzekCQjs90rVi^Ae(~tEA0DLR| zLiYdH`>!$nF5t}n&GFX{V*;x3N7S4jgX;YCQ}=5B(^J3y`qzk>Ffk%C6@c)gTi`%I zC_ntVqKJU7j0nB8QJvC|>=HlfJCb9BntdTsN^W@-w7Qb!ostOp68cCMB6Ob_d+gX| zamrXsgD#p@Y(YgzXtZQP>eL|hORSZuwsj0H6G8qWl~QQh3X@UJ^S6$NLEYQOo#|d* zyw@17f{>|*3SxxG1Vr;*9#3(P%L?g-DVs`M!nOsbCr)Yb+a7OQ%8KrP{6cnWW6h&B zEg^#$v9}F;hbRI;5(xVIbv6;|oU=0ru57+ywp5|kDl4hs7knPTk`9%>q6qIqw3j1X zXEjD$Ss7}G%cIyHS(a+?;t2<)#IC&vjwo`5^foSEbjDFQF*)NMZGp7-OTrL05sz?D z<>;9Akj|MMg<(LxDeC8)9bKe#(6o_EM{*R5NY${4l!+RWq zN5dAhRG*u(-l)mG*2(^Ia^pg#K{HJI{wN$5rSG=$$W(f+{-x{-qiA0t=(rXWCdOiw z0T+rR-&){&Y$oZ#Ht>bS7WqSvPaz#vOn3qXFx27MJ&%yI`!!ynOy#iKlI^idV?&N4 zga9>#cQm<9jE-jlHW0`*JBm`^#_I0`=nzqY6{4odcV4wuJ;`km$8knLNQcnwH;KJc ziMCSB({k8{rgQroZVV*FPMi8sU`Y3FtwQs54o(t^-9M4Mk7s!1WweND8fn9pafjy& zkGe6oS_7U&)Jyc8Pb<{Ro-l5L{SGHvr91V0GTwnX9;pjFOeCxcn);HHO21g7H|5eDQ_;P66I1d3!D}Th zFF6?nydgmi+J6i23nd>RXdz7H{CB!)TiaRvW}b%(lUUlM zya04&!r#*weI=>)pThkG1~FqBV+VbwpHh%XdN&=y0RaUA0*tVH#RuH${f)?efkOVb zEJ_cxbRRxBw@-j76VN7u0V$at|3Huj;QK?%|6l}1CuajkfR&8s?|=GUQTHTa69Mz5 z0B~V|Iqu`6eJ%C=$!xz&VrZssV`|KBA0_^$WFK4WWFr6!o&c%tP)gJ3x|e8R!7BDINMdcYA zng6J@ud|u*z*WLs05&5)DnJ(VZ>8S9>p!r#IT_phyp0>^Z?ioG14si4c$N2!<-Vta zJ^5v6evg6pv#vziQ+;d-pgD*6U2$KT>iug4{u_-hV9YZIj6XkP3liKg=K(0r9?)@r zRGz!&e_;Dd9&d5+;(UN8m;f$-7~e|0Kb-h4^H`ZXIx!fU85>#}*t-2xEZtwBmtT2aebN~?e0InZB#KM21GPl+@HFo@g>|TKT9z_l| zf_qPLfBh&|k>r1q>o2|Rz`$ecasbUa;QG;Gf>QsD%G%k=$^3q#{h1o|R(l|&0aRLm z>qoyN2l(e-^cBA~5Wt+v-1Z;2el*$nf8=sBwlX&SxqIqKD!RKT;Rjqlnk-A^zX|4~ zZ~Euf@o@b3enNC7z$N?Ns2+|UOfk{GY5+m*hr}PMp0-(bKM#vwX#gNT^W&3S0@UsR<`2vykBY^A~fK1>A(XMU(E7{-p zCMTf*sBQqwF+h_)(uv#uXF4muT;u001stPyED1mt{wF#MzzF)^40j)7+WDW|;g8d4 zJ;(n__s}7}!EYId0EmMMFc@G3_pJbCYroFnq7TsB{W|gcrg0DX(77{w;{gJ?6F>!c z<6l(XS22DDPW1!0{;z=l9W22Quton2`xWf#ZbHG*JIZwc2s=RC_dC5`OTGW*Ux5AQ z_`lmo_`7xZ%&gCm(mgW3kbu$PTdDU)bN#t`AEN(x34Raq&~&8Vknc@b>-I0P|G%^H zGep4n^{w0qoc%50L!CzW!d7bmBn|`Y1Os*hzLk1^diS5#>}wnSmuf{qhiuCL5V!&e zn7+Z^`|2gne@*c3G)wUv{GGrXzpaxXPXDhl{I>7(Gm7fnp^kNc0cHRTp&y6-u!UcvxX%}%YxG;fh@T3h zU7Wb?3;{@}iT<#~_OAaL)1RBu&umTm&C>T{RP@fTaXd73s1Sm!7ofkH19lwQzC*6} zpZ|Brzb~v9%mJRm;pcwiVV?T0EcO0{d;hBPUn#z2t^X>-&(w3D@%3xD6X-bk6^6gV zCHV>pxZeppI{jC;e+P*51EBcd0sb8f>JKo!e*yD%2=G53RQ(Cz>$6{b@52nTUmvgc zfA_<)-{q729^!td>|tuvKOi2Rf3Mgd60Lp(x&P&dGXa3R`d01)Mlb#Z^7Y|+A?|DN zFwy7Nr|bQ%e|T8+dnXdo}u1 zmiu45SH#0yKYu{Vzu)jQWbMN#2@A3hOfgX}QOgZoeSsDCaE97se66L|Tr;U8i?JYMt_v)+I8pE3W`D!=ue zzt_$8YtB0XN$G#o&7X)Kit)AD_xB%Fll-sI{|@uVHvjgYG5-z>^Sj#K31G?mwfKLB z0+^D0D|Z5NKSTNY_}7m2@U+s`=j;7_et7)b*`