|
|
|
|
<?php
|
|
|
|
|
// +----------------------------------------------------------------------
|
|
|
|
|
// | 徐州梦创信息科技有限公司—专业的游戏运营,推广解决方案.
|
|
|
|
|
// +----------------------------------------------------------------------
|
|
|
|
|
// | Copyright (c) 2013 http://www.vlcms.com All rights reserved.
|
|
|
|
|
// +----------------------------------------------------------------------
|
|
|
|
|
// | Author: kefu@vlcms.com QQ:97471547
|
|
|
|
|
// +----------------------------------------------------------------------
|
|
|
|
|
namespace Admin\Event;
|
|
|
|
|
|
|
|
|
|
use Think\Controller;
|
|
|
|
|
use Base\Service\GameSourceService;
|
|
|
|
|
use Base\Tool\PlistParser;
|
|
|
|
|
use Base\Tool\ApkParser;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 后台首页控制器
|
|
|
|
|
*
|
|
|
|
|
* @author 麦当苗儿 <zuojiazi@vip.qq.com>
|
|
|
|
|
*/
|
|
|
|
|
class SourceEvent extends Controller
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
public function add_source()
|
|
|
|
|
{
|
|
|
|
|
$data = $_REQUEST;
|
|
|
|
|
$data['file_size'] = round($data['file_size'] / pow(1024, 2), 2) . "MB";
|
|
|
|
|
$data['file_url'] = $data['file_url'] . "/" . $data['file_name'];
|
|
|
|
|
$data['sdk_version'] = $data['file_type'];
|
|
|
|
|
$data['op_id'] = UID;
|
|
|
|
|
$data['op_account'] = session("user_auth.username");
|
|
|
|
|
$data['create_time'] = NOW_TIME;
|
|
|
|
|
$remark = str_replace(array("\r\n", "\r", "\n"), "@@@", $_POST['remark']);
|
|
|
|
|
$data['remark'] = json_encode(explode('@@@', $remark));
|
|
|
|
|
$data['source_version'] = 0;
|
|
|
|
|
$version = '';
|
|
|
|
|
$packageName = '';
|
|
|
|
|
if ($data['file_type'] == 1) {
|
|
|
|
|
$parser = new ApkParser();
|
|
|
|
|
$parser->open($data['file_url']);
|
|
|
|
|
$version = $parser->getVersionName();
|
|
|
|
|
$packageName = $parser->getPackage();
|
|
|
|
|
} elseif($data['file_type'] == 2) {
|
|
|
|
|
$parser = new PlistParser();
|
|
|
|
|
$parser->openFromIpa($data['file_url'], PlistParser::PREG_INFO_PLIST);
|
|
|
|
|
$result = $parser->getResult();
|
|
|
|
|
$version = $result['CFBundleShortVersionString'];
|
|
|
|
|
$packageName = $result['CFBundleIdentifier'];
|
|
|
|
|
}
|
|
|
|
|
$data['version'] = $version;
|
|
|
|
|
$data['bao_name'] = $packageName;
|
|
|
|
|
$game = M('game', 'tab_')->where(['id' => $data['game_id']])->find();
|
|
|
|
|
$gameSourceService = new GameSourceService();
|
|
|
|
|
$result = $gameSourceService->sourcePack($data, $game);
|
|
|
|
|
if (!$result['status']) {
|
|
|
|
|
$this->error('添加失败' , U('GameSource/lists', ['type' => $data['type']]));
|
|
|
|
|
}
|
|
|
|
|
$data['plist_url'] = $result['data']['plistUrl'];
|
|
|
|
|
$data['org_plist_url'] = $result['data']['orgPlistUrl'];
|
|
|
|
|
$data['file_url'] = $result['data']['fileUrl'];
|
|
|
|
|
$data['original_url'] = $result['data']['originalUrl'];
|
|
|
|
|
if (M('game_source', 'tab_')->add($data)) {
|
|
|
|
|
//原包打包
|
|
|
|
|
$this->update_game_size($data);
|
|
|
|
|
$this->success('添加成功', U('GameSource/lists', ['type' => $data['type']]));
|
|
|
|
|
} else {
|
|
|
|
|
$this->error('添加失败', U('GameSource/lists', ['type' => $data['type']]));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*修改游戏原包
|
|
|
|
|
*/
|
|
|
|
|
public function update_source($id = null, $file_name, $from = "")
|
|
|
|
|
{
|
|
|
|
|
$id || $this -> error('id不能为空');
|
|
|
|
|
$model = D('Game_source');
|
|
|
|
|
$plist = A("Plist");
|
|
|
|
|
$data = $_REQUEST;
|
|
|
|
|
$url = $data['file_url'];
|
|
|
|
|
$urll = $data['file_url2'];
|
|
|
|
|
$data['file_size'] = strpos($data['file_size'], 'MB') ? $data['file_size'] : round($data['file_size'] / pow(1024, 2), 2) . "MB";
|
|
|
|
|
$data['file_url'] = $data['file_url'] . "/" . $data['file_name'];
|
|
|
|
|
$data['id'] = $id;
|
|
|
|
|
$data['op_id'] = UID;
|
|
|
|
|
$data['op_account'] = session("user_auth.username");
|
|
|
|
|
$data['create_time'] = NOW_TIME;
|
|
|
|
|
$remark = str_replace(array("\r\n", "\r", "\n"), "@@@", $_POST['remark']);
|
|
|
|
|
$data['remark'] = json_encode(explode('@@@', $remark));
|
|
|
|
|
$game_source = $model->where(['id' => $id])->find();
|
|
|
|
|
if($game_source['file_name'] == $data['file_name']) {
|
|
|
|
|
if ($from == "dev") {
|
|
|
|
|
$this -> success('修改成功', U('Developers/source'));
|
|
|
|
|
} else {
|
|
|
|
|
$this -> success('修改成功', U('GameSource/lists'));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$data['source_version'] = $game_source['source_version'] + 1;
|
|
|
|
|
|
|
|
|
|
$game = M('game', 'tab_')->where(['id' => $data['game_id']])->find();
|
|
|
|
|
$gameSourceService = new GameSourceService();
|
|
|
|
|
$result = $gameSourceService->sourcePack($data, $game);
|
|
|
|
|
if (!$result['status']) {
|
|
|
|
|
if ($from == "dev") {
|
|
|
|
|
$this->error('修改失败', U('Developers/source'));
|
|
|
|
|
} else {
|
|
|
|
|
$this->error('修改失败', U('GameSource/lists'));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$data['plist_url'] = $result['data']['plistUrl'];
|
|
|
|
|
$data['org_plist_url'] = $result['data']['orgPlistUrl'];
|
|
|
|
|
$data['file_url'] = $result['data']['fileUrl'];
|
|
|
|
|
$data['original_url'] = $result['data']['originalUrl'];
|
|
|
|
|
|
|
|
|
|
if ($model->save($data)) {
|
|
|
|
|
//渠道包加入打包队列
|
|
|
|
|
$appmodel = M('Apply', 'tab_');
|
|
|
|
|
$app_map['status'] = 1;
|
|
|
|
|
$app_map['game_id'] = $game_source['game_id'];
|
|
|
|
|
$app_data = $appmodel->where($app_map)->select();
|
|
|
|
|
if ($app_data) {
|
|
|
|
|
M()->startTrans();
|
|
|
|
|
$app_res = $appmodel->where($app_map)->save(['enable_status'=>2, 'pack_url'=>'', 'plist_url'=>'']);
|
|
|
|
|
M('apply_launch', 'tab_') -> where(['launch_game_id'=>$game_source['game_id']])->setField('launch_packge', 0);
|
|
|
|
|
if ($app_res === false) {
|
|
|
|
|
M()->rollback();
|
|
|
|
|
if ($from == "dev") {
|
|
|
|
|
$this->error('渠道包打包失败', U('Developers/source'));
|
|
|
|
|
} else {
|
|
|
|
|
$this->error('渠道包打包失败', U('GameSource/lists'));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
M()->commit();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@unlink($url . "/" . $file_name);
|
|
|
|
|
@unlink($urll . "/" . str_replace('.ipa', '.mobileprovision', $file_name));
|
|
|
|
|
|
|
|
|
|
$this->update_game_size($data);
|
|
|
|
|
|
|
|
|
|
//发送站内信
|
|
|
|
|
$users = M('ucenter_member')->where('status=1') -> field('id')->select();
|
|
|
|
|
$content = '游戏:' . $game['game_name'] . ' 已更新,请尽快处理渠道包';
|
|
|
|
|
D('Msg')->sendMsg2($users, $content, $game['sdk_version'], $game['id']);
|
|
|
|
|
|
|
|
|
|
if ($from == "dev") {
|
|
|
|
|
$this->success('修改成功', U('Developers/source'));
|
|
|
|
|
} else {
|
|
|
|
|
$this->success('修改成功', U('GameSource/lists'));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if ($from == "dev") {
|
|
|
|
|
$this->error('修改失败', U('Developers/source'));
|
|
|
|
|
} else {
|
|
|
|
|
$this->error('修改失败', U('GameSource/lists'));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function update_game_size($param = null)
|
|
|
|
|
{
|
|
|
|
|
$model = D('Game');
|
|
|
|
|
$map['id'] = $param['game_id'];
|
|
|
|
|
$data['game_size'] = $param['file_size'];
|
|
|
|
|
// $data['version'] = $param['version'];
|
|
|
|
|
if ($param['file_type'] == 1) {
|
|
|
|
|
$data['and_dow_address'] = $param['file_url'];
|
|
|
|
|
$ggame = $model -> where(array('id' => $map['id'])) -> find();
|
|
|
|
|
if ($ggame['sdk_version'] != - 1) {
|
|
|
|
|
if ($ggame['sdk_version'] == 2) {
|
|
|
|
|
$data['sdk_version'] = 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$data['sdk_version'] = 1;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if ($ggame['sdk_version'] != - 1) {
|
|
|
|
|
if ($ggame['sdk_version'] == 1) {
|
|
|
|
|
$data['sdk_version'] = 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$data['sdk_version'] = 2;
|
|
|
|
|
}
|
|
|
|
|
$data['ios_dow_address'] = $param['file_url'];
|
|
|
|
|
}
|
|
|
|
|
$model -> where($map) -> save($data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*原包打包
|
|
|
|
|
*/
|
|
|
|
|
protected function soure_pack($game_id = 0, $file_url = "", $file_type = 1, $source_version = 1, $baoName='', $flag=0, $file_name='')
|
|
|
|
|
{
|
|
|
|
|
$game_info = M("game", "tab_") -> find($game_id);
|
|
|
|
|
$data = array(
|
|
|
|
|
"game_id" => $game_info['id'],
|
|
|
|
|
"game_name" => $game_info['game_name'],
|
|
|
|
|
"game_appid" => $game_info['game_appid'],
|
|
|
|
|
"promote_id" => 0,
|
|
|
|
|
"promote_account" => "自然注册",
|
|
|
|
|
'source_version' => $source_version,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$real_file_url = ROOTTT.ltrim($file_url, './');
|
|
|
|
|
$temp = explode('/', $file_url);
|
|
|
|
|
$newFileName = $temp[count($temp)-1];
|
|
|
|
|
|
|
|
|
|
$to = $file_url;
|
|
|
|
|
$originalUrl = '';
|
|
|
|
|
if ($file_type == 1) {
|
|
|
|
|
$new_file_url = $real_file_url;
|
|
|
|
|
$zip = new \ZipArchive;
|
|
|
|
|
$res = $zip -> open($real_file_url, \ZipArchive::CREATE);
|
|
|
|
|
$zip -> addFromString('META-INF/mch.properties', json_encode($data));
|
|
|
|
|
$zip -> close();
|
|
|
|
|
} elseif ($file_type == 2) {
|
|
|
|
|
if (preg_match("/.ipa/", $file_url)) {
|
|
|
|
|
if($flag == 0 || ($flag ==1 && $newFileName != $file_name)) {
|
|
|
|
|
$originalUrl = $file_url;
|
|
|
|
|
$new_file_url = str_replace('Uploads/SourcePack', 'Uploads/Ios/original', $real_file_url);
|
|
|
|
|
$to = str_replace('Uploads/SourcePack', 'Uploads/Ios/original', $file_url);
|
|
|
|
|
$zip_open_game_source = zip_open($real_file_url);
|
|
|
|
|
if ($zip_open_game_source) {
|
|
|
|
|
while ($zip_entry = zip_read($zip_open_game_source)) {
|
|
|
|
|
if (preg_match("/.app/", zip_entry_name($zip_entry))) {
|
|
|
|
|
$ios_app = substr(zip_entry_name($zip_entry), 8) . "<br/>";
|
|
|
|
|
}
|
|
|
|
|
$new_ios_1 = explode("/", $ios_app);
|
|
|
|
|
}
|
|
|
|
|
zip_close($zip_open_game_source);
|
|
|
|
|
}
|
|
|
|
|
$url_ver = "Payload/" . $new_ios_1[0] . "/_CodeSignature/TXChannel";
|
|
|
|
|
$zip = new \ZipArchive;
|
|
|
|
|
copy($real_file_url, $new_file_url);
|
|
|
|
|
$res = $zip -> open($new_file_url, \ZipArchive::CREATE);
|
|
|
|
|
$zip -> addFromString($url_ver, json_encode($data));
|
|
|
|
|
$zip -> close();
|
|
|
|
|
// unlink($real_file_url);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$new_path = str_replace("./Uploads/", "", $file_url);
|
|
|
|
|
if (get_tool_status("oss_storage") == 1) {
|
|
|
|
|
|
|
|
|
|
if(C("oss_storage.is_cname")){
|
|
|
|
|
$to = "http://".C("oss_storage.domain")."/".str_replace("Ios/original","SourcePack",$new_path);
|
|
|
|
|
}else{
|
|
|
|
|
$to = "https://".C("oss_storage.bucket").".".C("oss_storage.domain")."/".str_replace("Ios/original","SourcePack",$new_path);
|
|
|
|
|
$to = str_replace('-internal', '', $to);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$updata['savename'] = $newFileName;
|
|
|
|
|
$updata['path'] = $new_file_url;
|
|
|
|
|
$this -> upload_game_pak_oss($updata);
|
|
|
|
|
} elseif (get_tool_status("cos_storage") == 1) {
|
|
|
|
|
$updata['path'] = $new_file_url;
|
|
|
|
|
$updata['savename'] = 'SourcePack/'. $newFileName;
|
|
|
|
|
$to = $this->upload_cos($updata);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$model = D('Game_source');
|
|
|
|
|
$model->where(['game_id'=>$game_id])->save(['file_url'=>$to, 'original_url' => $originalUrl]);
|
|
|
|
|
|
|
|
|
|
$plist = A("Plist");
|
|
|
|
|
|
|
|
|
|
if ($file_type == 2) {
|
|
|
|
|
$data['plist_url'] = "./Uploads/SourcePlist/" . $data['game_id'] . ".Plist";
|
|
|
|
|
$plist -> create_plist($game_id, 0, $baoName, $to);
|
|
|
|
|
$plist -> create_org_plist($game_id, 0, $baoName, $originalUrl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function upload_game_pak_oss($return_data = null)
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* 根据Config配置,得到一个OssClient实例
|
|
|
|
|
*/
|
|
|
|
|
try {
|
|
|
|
|
Vendor('OSS.autoload');
|
|
|
|
|
$ossClient = new \OSS\OssClient(C("oss_storage.accesskeyid"), C("oss_storage.accesskeysecr"), C("oss_storage.domain"), C("oss_storage.is_cname"));
|
|
|
|
|
} catch (OssException $e) {
|
|
|
|
|
$this -> error($e->getMessage());
|
|
|
|
|
}
|
|
|
|
|
$bucket = C('oss_storage.bucket');
|
|
|
|
|
$oss_name = "SourcePack";
|
|
|
|
|
$oss_file_path = $oss_name . "/" . $return_data["savename"];
|
|
|
|
|
$avatar = $return_data["path"];
|
|
|
|
|
try {
|
|
|
|
|
$this->multiuploadFile($ossClient, $bucket, $oss_file_path, $avatar);
|
|
|
|
|
return true;
|
|
|
|
|
} catch (OssException $e) {
|
|
|
|
|
/* 返回JSON数据 */
|
|
|
|
|
$this->error($e -> getMessage());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function multiuploadFile($ossClient, $bucket, $url, $file)
|
|
|
|
|
{
|
|
|
|
|
//$file = __FILE__;
|
|
|
|
|
$options = array();
|
|
|
|
|
try {
|
|
|
|
|
#初始化分片上传文件
|
|
|
|
|
$uploadId = $ossClient -> initiateMultipartUpload($bucket, $url);
|
|
|
|
|
//$ossClient->multiuploadFile($bucket, $url, $file, $options);
|
|
|
|
|
} catch (OssException $e) {
|
|
|
|
|
printf(__FUNCTION__ . ": initiateMultipartUpload FAILED\n");
|
|
|
|
|
$this -> error('1' . $e -> getMessage() . "\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* step 2. 上传分片
|
|
|
|
|
*/
|
|
|
|
|
$partSize = 5 * 1000 * 1024;
|
|
|
|
|
$uploadFile = $file;
|
|
|
|
|
$uploadFileSize = filesize($uploadFile);
|
|
|
|
|
$pieces = $ossClient -> generateMultiuploadParts($uploadFileSize, $partSize);
|
|
|
|
|
$responseUploadPart = array();
|
|
|
|
|
$uploadPosition = 0;
|
|
|
|
|
$isCheckMd5 = true;
|
|
|
|
|
foreach ($pieces as $i => $piece) {
|
|
|
|
|
$fromPos = $uploadPosition + (integer) $piece[$ossClient ::OSS_SEEK_TO];
|
|
|
|
|
$toPos = (integer) $piece[$ossClient ::OSS_LENGTH] + $fromPos - 1;
|
|
|
|
|
$upOptions = array(
|
|
|
|
|
$ossClient ::OSS_FILE_UPLOAD => $uploadFile,
|
|
|
|
|
$ossClient ::OSS_PART_NUM => ($i + 1),
|
|
|
|
|
$ossClient ::OSS_SEEK_TO => $fromPos,
|
|
|
|
|
$ossClient ::OSS_LENGTH => $toPos - $fromPos + 1,
|
|
|
|
|
$ossClient ::OSS_CHECK_MD5 => $isCheckMd5,
|
|
|
|
|
);
|
|
|
|
|
if ($isCheckMd5) {
|
|
|
|
|
$contentMd5 = \OSS\Core\OssUtil ::getMd5SumForFile($uploadFile, $fromPos, $toPos);
|
|
|
|
|
$upOptions[$ossClient ::OSS_CONTENT_MD5] = $contentMd5;
|
|
|
|
|
}
|
|
|
|
|
//2. 将每一分片上传到OSS
|
|
|
|
|
try {
|
|
|
|
|
$responseUploadPart[] = $ossClient -> uploadPart($bucket, $url, $uploadId, $upOptions);
|
|
|
|
|
} catch (OssException $e) {
|
|
|
|
|
printf(__FUNCTION__ . ": initiateMultipartUpload, uploadPart - part#{$i} FAILED\n");
|
|
|
|
|
$this -> error($e -> getMessage() . "\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$uploadParts = array();
|
|
|
|
|
foreach ($responseUploadPart as $i => $eTag) {
|
|
|
|
|
$uploadParts[] = array(
|
|
|
|
|
'PartNumber' => ($i + 1),
|
|
|
|
|
'ETag' => $eTag,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* step 3. 完成上传
|
|
|
|
|
*/
|
|
|
|
|
try {
|
|
|
|
|
$ossClient -> completeMultipartUpload($bucket, $url, $uploadId, $uploadParts);
|
|
|
|
|
} catch (OssException $e) {
|
|
|
|
|
printf(__FUNCTION__ . ": completeMultipartUpload FAILED\n");
|
|
|
|
|
$this -> error($e -> getMessage() . "\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 腾讯云上传
|
|
|
|
|
*
|
|
|
|
|
* @param null $data
|
|
|
|
|
*
|
|
|
|
|
* @return mixed
|
|
|
|
|
*
|
|
|
|
|
* @author: 鹿文学[lwx]<fyj301415926@126.com>
|
|
|
|
|
* @since: 2019\4\18 0018 17:33
|
|
|
|
|
*/
|
|
|
|
|
public function upload_cos($data=null) {
|
|
|
|
|
|
|
|
|
|
Vendor('COS5.vendor.autoload');
|
|
|
|
|
|
|
|
|
|
$schema=C("cos_storage.schema")?:"https";
|
|
|
|
|
|
|
|
|
|
$secretId = C("cos_storage.SecretId"); //"云 API 密钥 SecretId";
|
|
|
|
|
$secretKey = C("cos_storage.SecretKey"); //"云 API 密钥 SecretKey";
|
|
|
|
|
$region = C("cos_storage.domain"); //设置一个默认的存储桶地域
|
|
|
|
|
$cosClient = new \Qcloud\Cos\Client(
|
|
|
|
|
array(
|
|
|
|
|
'region' => $region,
|
|
|
|
|
'schema' => $schema, //协议头部,默认为http
|
|
|
|
|
'credentials'=> array(
|
|
|
|
|
'secretId' => $secretId ,
|
|
|
|
|
'secretKey' => $secretKey
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$bucket = C("cos_storage.bucket"); //存储桶名称 格式:BucketName-APPID
|
|
|
|
|
$key = $data['savename'];
|
|
|
|
|
$srcPath = $data["path"];//本地文件绝对路径
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$result = $cosClient->Upload(
|
|
|
|
|
$bucket = $bucket,
|
|
|
|
|
$key = $key,
|
|
|
|
|
$body = fopen($srcPath, 'rb')
|
|
|
|
|
);
|
|
|
|
|
$location = $result['Location'];
|
|
|
|
|
if(!preg_match('/^(http)|(https)/', $location)) {
|
|
|
|
|
$result['Location'] = 'https://' . $result['Location'];
|
|
|
|
|
}
|
|
|
|
|
return $result['Location'];
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
$this->error($e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|