diff --git a/app/Config/Constants.php b/app/Config/Constants.php index fb56bb1..bf093d1 100644 --- a/app/Config/Constants.php +++ b/app/Config/Constants.php @@ -35,11 +35,11 @@ defined('COMPOSER_PATH') || define('COMPOSER_PATH', ROOTPATH . 'vendor/autoload. */ defined('SECOND') || define('SECOND', 1); defined('MINUTE') || define('MINUTE', 60); -defined('HOUR') || define('HOUR', 3600); -defined('DAY') || define('DAY', 86400); -defined('WEEK') || define('WEEK', 604800); -defined('MONTH') || define('MONTH', 2_592_000); -defined('YEAR') || define('YEAR', 31_536_000); +defined('HOUR') || define('HOUR', 3600); +defined('DAY') || define('DAY', 86400); +defined('WEEK') || define('WEEK', 604800); +defined('MONTH') || define('MONTH', 2_592_000); +defined('YEAR') || define('YEAR', 31_536_000); defined('DECADE') || define('DECADE', 315_360_000); /* @@ -67,13 +67,23 @@ defined('DECADE') || define('DECADE', 315_360_000); | http://tldp.org/LDP/abs/html/exitcodes.html | */ -defined('EXIT_SUCCESS') || define('EXIT_SUCCESS', 0); // no errors -defined('EXIT_ERROR') || define('EXIT_ERROR', 1); // generic error -defined('EXIT_CONFIG') || define('EXIT_CONFIG', 3); // configuration error -defined('EXIT_UNKNOWN_FILE') || define('EXIT_UNKNOWN_FILE', 4); // file not found -defined('EXIT_UNKNOWN_CLASS') || define('EXIT_UNKNOWN_CLASS', 5); // unknown class +defined('EXIT_SUCCESS') || define('EXIT_SUCCESS', 0); // no errors +defined('EXIT_ERROR') || define('EXIT_ERROR', 1); // generic error +defined('EXIT_CONFIG') || define('EXIT_CONFIG', 3); // configuration error +defined('EXIT_UNKNOWN_FILE') || define('EXIT_UNKNOWN_FILE', 4); // file not found +defined('EXIT_UNKNOWN_CLASS') || define('EXIT_UNKNOWN_CLASS', 5); // unknown class defined('EXIT_UNKNOWN_METHOD') || define('EXIT_UNKNOWN_METHOD', 6); // unknown class member -defined('EXIT_USER_INPUT') || define('EXIT_USER_INPUT', 7); // invalid user input -defined('EXIT_DATABASE') || define('EXIT_DATABASE', 8); // database error -defined('EXIT__AUTO_MIN') || define('EXIT__AUTO_MIN', 9); // lowest automatically-assigned error code -defined('EXIT__AUTO_MAX') || define('EXIT__AUTO_MAX', 125); // highest automatically-assigned error code +defined('EXIT_USER_INPUT') || define('EXIT_USER_INPUT', 7); // invalid user input +defined('EXIT_DATABASE') || define('EXIT_DATABASE', 8); // database error +defined('EXIT__AUTO_MIN') || define('EXIT__AUTO_MIN', 9); // lowest automatically-assigned error code +defined('EXIT__AUTO_MAX') || define('EXIT__AUTO_MAX', 125); // highest automatically-assigned error code + + +/** + * ncloud url + */ +define('NCLOUD_OBJECT_STORAGE_URL', 'https://kr.object.ncloudstorage.com/confirms-object'); +define('NCLOUD_S3_KEY', 'ncp_iam_BPAMKR3l50hXJiQ6qpSP'); +define('NCLOUD_S3_SECRET', 'ncp_iam_BPKMKRW2GU59UE59I1QftVGst6NJgnmbSc'); +define('NCLOUD_S3_BUCKET', 'confirms-object'); +define('NCLOUD_S3_ENDPOINT', 'https://kr.object.ncloudstorage.com'); \ No newline at end of file diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 37208f5..1766f1d 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -43,6 +43,44 @@ $routes->group('board', ['namespace' => 'App\Controllers\Board'], function ($rou }); +/** + * 아파트단지 DB구축 그룹 + */ +$routes->group('article', ['namespace' => 'App\Controllers\Article'], function ($routes) { + // 아파트단지DB구축현황 + $routes->get('apt/lists', 'Apt::lists'); + $routes->get('apt/detail/(:num)', 'Apt::detail/$1'); + + // 관할포인트 인쇄 + $routes->get('apt/print', 'Apt::print'); + + + /** API - 아파트단지 */ + $routes->get('apt/getAptLists', 'Apt::getAptLists'); + $routes->post('apt/saveAptMemo', 'Apt::saveAptMemo'); + $routes->post('apt/chgAptDamdang', 'Apt::chgAptDamdang'); + $routes->post('apt/chgAptVideoTarget', 'Apt::chgAptVideoTarget'); + $routes->post('apt/chkTakeAptPhotoCnt', 'Apt::chkTakeAptPhotoCnt'); + $routes->get('apt/excel', 'Apt::excel'); + + /** API - 아파트단지 상세 */ + $routes->post('apt/saveKeeper', 'Apt::saveKeeper'); + $routes->post('apt/saveCoordinate', 'Apt::saveCoordinate'); + $routes->post('apt/saveNote', 'Apt::saveNote'); + $routes->post('apt/saveVideoTarget', 'Apt::saveVideoTarget'); + $routes->post('apt/saveVideoReason', 'Apt::saveVideoReason'); + $routes->get('apt/cateJson', 'Apt::cateJson'); + $routes->post('apt/savePhoReason', 'Apt::savePhoReason'); + $routes->post('apt/saveCate', 'Apt::saveCate'); + $routes->post('apt/savePhotoView', 'Apt::savePhotoView'); + $routes->post('apt/savePhotoView', 'Apt::savePhotoView'); + $routes->post('apt/confirmAptInfo', 'Apt::confirmAptInfo'); + $routes->post('apt/resendAptInfo', 'Apt::resendAptInfo'); + + $routes->post('apt/uploadFile', 'Apt::uploadFile'); + +}); + /** * 실적관리 (results) 그룹 */ diff --git a/app/Controllers/Article/Apt.php b/app/Controllers/Article/Apt.php new file mode 100644 index 0000000..e431459 --- /dev/null +++ b/app/Controllers/Article/Apt.php @@ -0,0 +1,995 @@ +aptModel = new AptModel(); + $this->codeModel = new CodeModel(); + } + + // 아파트단지 + public function lists(): string + { + $codes = $this->codeModel->getCodeLists(['VIDEO_TARGET', 'APT_STEP', 'PHO_YN', 'VDO_YN']); // 코드조회 + $sido = $this->aptModel->getAreaList(); // 지역조회 + $bonbu = $this->aptModel->getBonbuList(); // 본부 + $team = $this->aptModel->getTeamList(); // 팀 + $user = $this->aptModel->getUserList(); // 유저 + + return view("pages/article/lists", [ + 'codes' => $codes, + 'sido' => $sido, + 'bonbu' => $bonbu, + 'team' => $team, + 'user' => $user, + ]); + } + + // 아파트단지목록 조회 + public function getAptLists() + { + + $start = (int) $this->request->getGet('start') ?: 0; + $end = (int) $this->request->getGet('length') ?: 10; + + $data = [ + 'hscp_no' => $this->request->getGet('hscp_no'), // 단지코드 + 'pho_no' => $this->request->getGet('pho_no'), // 사진코드 + 'srcSido' => $this->request->getGet('srcSido'), // 시|도 + 'srcGugun' => $this->request->getGet('srcGugun'), // 시|군|구 + 'srcDong' => $this->request->getGet('srcDong'), // 읍|면|동 + + 'rcpt_hscp_nm' => $this->request->getGet('rcpt_hscp_nm'), // 단지명 + 'sdate' => $this->request->getGet('sdate'), // 시작일 + 'edate' => $this->request->getGet('edate'), // 종료일 + 'households_cnt1' => $this->request->getGet('households_cnt1'), // 총세대수1 + 'households_cnt2' => $this->request->getGet('households_cnt2'), // 총세대수2 + 'dong_cnt1' => $this->request->getGet('dong_cnt1'), // 총동수1 + 'dong_cnt2' => $this->request->getGet('dong_cnt2'), // 총동수2 + + 'bonbu' => $this->request->getGet('bonbu'), // 본부 + 'team' => $this->request->getGet('team'), // 팀 + 'damdang' => $this->request->getGet('damdang'), // 담당 + + 'stat' => $this->request->getGet('stat'), // 진행상태 + ]; + + $totalCount = $this->aptModel->getTotalCount($data); + $datas = $this->aptModel->getAptLists($start, $end, $data); + + $deptStatistics = $this->aptModel->getDeptStatistics($data); // 조직별통계 + $areaStatistics = $this->aptModel->getStatistics($data); // 지역별통계 + + return $this->response->setJSON(body: [ + 'draw' => (int) ($this->request->getGetPost('draw') ?? 0), // 서버사이드면 권장 + 'recordsTotal' => $totalCount, + 'recordsFiltered' => $totalCount, + 'data' => $datas, + 'widgets' => [ + 'deptList' => $deptStatistics, + 'areaStats' => $areaStatistics, + ], + ]); + + } + + // 전체촬영불가 단지 확인 + public function chkTakeAptPhotoCnt() + { + $data = $this->aptModel->chkTakeAptPhotoCnt(); + + return $this->response->setJSON( + body: + $data + ); + } + + // 관할포인트 인쇄 - 화면 + public function print(): string + { + $deptSq = $this->request->getGet('depChk'); + $dept_cnt = count($deptSq); + + + $listDept = $this->aptModel->getDeptMapList($deptSq); + + if (!empty($listDept)) { + $lati = 0; + $long = 0; + foreach ($listDept as $dept) { + $lati += $dept['rcpt_y']; + $long += $dept['rcpt_x']; + } + + $lati = $lati / $dept_cnt; + $long = $long / $dept_cnt; + } + + return view("pages/article/printMap", [ + // 'lati' => $lati, + // 'long' => $long, + 'listDept' => $listDept, + ]); + } + + // 아파트단지 정보저장 + public function saveAptMemo() + { + try { + + $rcpt_no = $this->request->getPost('rcpt_no'); + $video_target = $this->request->getPost('video_target'); + + if (empty($rcpt_no)) { + throw new Exception("단지정보 누락"); + } + + $params = [ + 'target' => $video_target, + 'memo' => $this->request->getPost('memo') ?: '', + 'rcpt_no' => $rcpt_no, + ]; + + // UPDATE apt_result + $this->aptModel->saveAptMemo($params); + + + return $this->response->setJSON([ + 'code' => '0', + 'msg' => 'success' + ]); + + + } catch (\Exception $e) { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => $e->getMessage(), + ]); + } + } + + // 담당자정보변경 + public function chgAptDamdang() + { + try { + + $team = $this->request->getPost('team'); + $damdang = $this->request->getPost(index: 'damdang'); + + if (empty($team)) { + throw new Exception("팀정보 누락"); + } + + if (empty($damdang)) { + throw new Exception("담당자정보 누락"); + } + + + $rows = $this->request->getPost('rows'); + + $rows = json_decode($rows, true); + + if (count($rows) > 0) { + foreach ($rows as $row) { + $params = [ + $team, + $damdang, + $row['rcpt_no'], + ]; + + + // UPDATE apt_result + $this->aptModel->updateAptDamdang($params); + + } + } else { + throw new Exception("저장할 데이터 누락"); + } + + return $this->response->setJSON([ + 'code' => '0', + 'msg' => 'success' + ]); + + + } catch (\Exception $e) { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => $e->getMessage(), + ]); + } + } + + + // 영상담당 정보 저장 + public function chgAptVideoTarget() + { + try { + + $target = $this->request->getPost('video'); + + if (empty($target)) { + throw new Exception("영상대상 정보 누락"); + } + + + $rows = $this->request->getPost('rows'); + + $rows = json_decode($rows, true); + + if (count($rows) > 0) { + foreach ($rows as $row) { + $params = [ + 'target' => $target, + 'apt_step' => $row['apt_step'], + 'rcpt_no' => $row['rcpt_no'], + 'usr_id' => session('usr_id'), + ]; + + + // UPDATE apt_result + $this->aptModel->updateAptVideoTarget($params); + + } + } else { + throw new Exception("저장할 데이터 누락"); + } + + return $this->response->setJSON([ + 'code' => '0', + 'msg' => 'success' + ]); + + + } catch (\Exception $e) { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => $e->getMessage(), + ]); + } + } + + + // 엑셀 다운로드 + public function excel() + { + + + try { + + $data = [ + 'hscp_no' => $this->request->getGet('hscp_no'), // 단지코드 + 'pho_no' => $this->request->getGet('pho_no'), // 사진코드 + 'srcSido' => $this->request->getGet('srcSido'), // 시|도 + 'srcGugun' => $this->request->getGet('srcGugun'), // 시|군|구 + 'srcDong' => $this->request->getGet('srcDong'), // 읍|면|동 + + 'rcpt_hscp_nm' => $this->request->getGet('rcpt_hscp_nm'), // 단지명 + 'sdate' => $this->request->getGet('sdate'), // 시작일 + 'edate' => $this->request->getGet('edate'), // 종료일 + 'households_cnt1' => $this->request->getGet('households_cnt1'), // 총세대수1 + 'households_cnt2' => $this->request->getGet('households_cnt2'), // 총세대수2 + 'dong_cnt1' => $this->request->getGet('dong_cnt1'), // 총동수1 + 'dong_cnt2' => $this->request->getGet('dong_cnt2'), // 총동수2 + + 'bonbu' => $this->request->getGet('bonbu'), // 본부 + 'team' => $this->request->getGet('team'), // 팀 + 'damdang' => $this->request->getGet('damdang'), // 담당 + + 'stat' => $this->request->getGet('stat'), // 진행상태 + ]; + + $datas = $this->aptModel->getExcelList($data); + + return $this->response->setJSON(body: [ + 'data' => $datas, + ]); + + } catch (\Exception $e) { + $e->getPrevious()->getTraceAsString(); + } + } + + + // 아파트단지 상세화면 + public function detail($id = null): string + { + $rcpt_no = (int) $id; + + if ($rcpt_no == null) { + throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + } + + $bonbu = $this->aptModel->getBonbuList(); + $team = $this->aptModel->getTeamList(); + $user = $this->aptModel->getUserList(); + $codes = $this->codeModel->getCodeLists(['VIDEO_TARGET', 'PHO_CATE1', 'PHO_CATE2']); // 코드조회 + + $apt = $this->aptModel->getDetail($rcpt_no); + + //이미지 리스트 + $image = $this->aptModel->getImgList($rcpt_no); + + //이미지 총 개수 + $cntAllPho = $this->aptModel->cntAllPho($rcpt_no); + if (empty($cntAllPho)) { + $cntAllPho = 0; + } + //동영상 리스트 + $vdo = $this->aptModel->getVideoList($rcpt_no); + //사진및 설명정보의 카테고리 리스트 + $cateInfo = $this->aptModel->getCateInfoList($rcpt_no); + + //정보변경이력 + $history = $this->aptModel->getHistory($rcpt_no); + + $code1 = []; + $code2 = []; + $video = []; + if (!empty($codes)) { + foreach ($codes as $code) { + if ($code['category'] === "PHO_CATE1") { + array_push($code1, $code); + } else if ($code['category'] === "PHO_CATE2") { + array_push($code2, $code); + } else if ($code['category'] === "VIDEO_TARGET") { + array_push($video, $code); + } + } + } + + // return print_r($image); + + return view("pages/article/detail", [ + 'apt' => $apt, + 'bonbu' => $bonbu, + 'team' => $team, + 'user' => $user, + 'code1' => $code1, + 'code2' => $code2, + 'video' => $video, + 'image' => $image, + 'vdo' => $vdo, + 'history' => $history, + 'cateInfo' => $cateInfo, + 'cntAllPho' => $cntAllPho + ]); + } + + public function cateJson() + { + $params = [ + $this->request->getGet('pho_cate1'), + ]; + + $datas = $this->aptModel->getCateJson($params); + + + return $this->response->setJSON($datas); + } + + + // 담당자 정보 저장 + public function saveKeeper() + { + try { + + $params = [ + 'rcpt_no' => $this->request->getPost('rcpt_no'), + 'bonbu' => $this->request->getPost('bonbu'), + 'team' => $this->request->getPost('team'), + 'user' => $this->request->getPost('user'), + ]; + + // UPDATE apt_result + $this->aptModel->saveKeeper($params); + + + return $this->response->setJSON([ + 'code' => '0', + 'msg' => 'success' + ]); + + } catch (\Exception $e) { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => $e->getMessage(), + ]); + } + } + + // 지도좌표 수정 + public function saveCoordinate() + { + try { + + $params = [ + 'rcpt_no' => $this->request->getPost('rcpt_no'), + 'rcpt_x' => $this->request->getPost('rcpt_x'), + 'rcpt_y' => $this->request->getPost('rcpt_y'), + ]; + + // UPDATE apt_receipt, apt_result + $this->aptModel->saveCoordinate($params); + + + return $this->response->setJSON([ + 'code' => '0', + 'msg' => 'success' + ]); + + } catch (\Exception $e) { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => $e->getMessage(), + ]); + } + } + + // 단지 특이사항 저장 + public function saveNote() + { + try { + + $params = [ + 'rcpt_no' => $this->request->getPost('rcpt_no'), + 'note' => $this->request->getPost('note'), + ]; + + // UPDATE apt_result + $this->aptModel->saveNote($params); + + + return $this->response->setJSON([ + 'code' => '0', + 'msg' => 'success' + ]); + + } catch (\Exception $e) { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => $e->getMessage(), + ]); + } + } + + + // 동영상 촬영정보 저장 + public function saveVideoTarget() + { + try { + + $params = [ + 'rcpt_no' => $this->request->getPost('rcpt_no'), + 'target' => $this->request->getPost('video_target'), + ]; + + // UPDATE apt_result + $this->aptModel->saveVideoTarget($params); + + + return $this->response->setJSON([ + 'code' => '0', + 'msg' => 'success' + ]); + + } catch (\Exception $e) { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => $e->getMessage(), + ]); + } + } + + // 촬영불가사유 저장 + public function saveVideoReason() + { + try { + + $params = [ + 'rcpt_no' => $this->request->getPost('rcpt_no'), + 'not_vdo_reson' => $this->request->getPost('not_vdo_reson'), + 'vdo_up_ynx' => $this->request->getPost('vdo_up_ynx'), + ]; + + // UPDATE apt_result + $this->aptModel->saveVideoReason($params); + + + return $this->response->setJSON([ + 'code' => '0', + 'msg' => 'success' + ]); + + } catch (\Exception $e) { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => $e->getMessage(), + ]); + } + } + + // 사진업로드 불가사유 저장 + public function savePhoReason() + { + try { + + $params = [ + 'rcpt_no' => $this->request->getPost('rcpt_no'), + 'pho_cate2' => $this->request->getPost('pho_cate2'), + 'pho_up_nu' => $this->request->getPost('pho_up_nu'), + 'usr_id' => session('usr_id'), + ]; + + // UPDATE apt_category + $this->aptModel->savePhoReason($params); + + + return $this->response->setJSON([ + 'code' => '0', + 'msg' => 'success' + ]); + + } catch (\Exception $e) { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => $e->getMessage(), + ]); + } + } + + // 사진 카테고리 수정 + public function saveCate() + { + try { + $phoNo = $this->request->getPost('pho_no'); + if (!is_array($phoNo)) { + $phoNo = [$phoNo]; + } + + $params = [ + 'pho_no' => $phoNo, + 'pho_cate1' => $this->request->getPost('pho_cate1'), + 'pho_cate2' => $this->request->getPost('pho_cate2'), + 'rcpt_no' => $this->request->getPost('rcpt_no'), + 'now_cate' => $this->request->getPost('nowCate2'), + ]; + + $this->aptModel->saveCate($params); + + return $this->response->setJSON([ + 'code' => '0', + 'msg' => 'success' + ]); + + } catch (\Exception $e) { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => $e->getMessage(), + ]); + } + + } + + // 사진 노출정보 저장 + public function savePhotoView() + { + try { + $phoNo = $this->request->getPost('pho_no'); + if (!is_array($phoNo)) { + $phoNo = [$phoNo]; + } + + $pho_cate2 = $this->request->getPost('pho_cate2'); + + $params = [ + 'pho_no' => $phoNo, + 'pho_view_yn' => $this->request->getPost('pho_view_yn_' . $pho_cate2), + 'rcpt_no' => $this->request->getPost('rcpt_no'), + ]; + + $this->aptModel->savePhotoView($params); + + return $this->response->setJSON([ + 'code' => '0', + 'msg' => 'success' + ]); + + } catch (\Exception $e) { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => $e->getMessage(), + ]); + } + } + + // 사진정보삭제 + public function removePhoto() + { + try { + $phoNo = $this->request->getPost('pho_no'); + if (!is_array($phoNo)) { + $phoNo = [$phoNo]; + } + + $pho_cate2 = $this->request->getPost('pho_cate2'); + + $params = [ + 'pho_no' => $phoNo, + 'rcpt_no' => $this->request->getPost('rcpt_no'), + 'cate2_cd' => $this->request->getPost('cate2_cd'), + ]; + + $this->aptModel->removePhoto($params); + + return $this->response->setJSON([ + 'code' => '0', + 'msg' => 'success' + ]); + + } catch (\Exception $e) { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => $e->getMessage(), + ]); + } + } + + // 사진설명 저장 + public function savePhoExplain() + { + try { + + $params = [ + 'rcpt_no' => $this->request->getPost('rcpt_no'), + 'pho_cate2' => $this->request->getPost('pho_cate2'), + 'pho_explain' => session('pho_explain'), + 'usr_id' => session('usr_id'), + ]; + + $this->aptModel->savePhoExplain($params); + + + return $this->response->setJSON([ + 'code' => '0', + 'msg' => 'success' + ]); + + } catch (\Exception $e) { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => $e->getMessage(), + ]); + } + } + + // 검수완료 저장 + public function confirmAptInfo() + { + try { + + $rcpt_no = $this->request->getPost('rcpt_no'); + $hscp_no = $this->request->getPost('hscp_no'); + + $params = [ + 'rcpt_no' => $rcpt_no, + 'hscp_no' => $hscp_no, + ]; + + $apiData = $this->aptModel->new_api_photo_send_data($rcpt_no); + + $ip_addr = array( + '172.16.100.2', + '172.16.100.3', + '172.16.100.4', + '172.16.100.5' + ); + + if ( + isset($_SERVER['HTTPS']) && + ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) || + isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && + $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' + ) { + $protocol = 'https://'; + } else { + $protocol = 'http://'; + } + + if (in_array($_SERVER['SERVER_ADDR'], $ip_addr) == false) { + $domain = $protocol . $_SERVER['HTTP_HOST']; + // $domain = $protocol . 'test-admin.confirms.co.kr'; + } else { + $domain = $protocol . 'admin.confirms.co.kr'; + } + + + foreach ($apiData as $key => $d) { + $isExposed = ($d['use_yn'] == 'Y' ? true : false); + $array[] = array('mainCategory' => $d['cate1_cd'], 'subCategory' => $d['cate2_cd'], 'comment' => $d['pho_explain']); + $array1[] = array( + 'photoNumber' => $d['pho_no'], + 'mainCategory' => $d['cate1_cd'], + 'subCategory' => $d['cate2_cd'], + 'lat' => $d['pho_lati'], + 'lng' => $d['pho_long'], + 'url' => $d['url'], + 'isExposed' => $isExposed, + 'order' => 1 + ); + } + + $serializedArray = array_map('serialize', $array); + $uniqueArray = array_unique($serializedArray); + $post_data_array['categories'] = array_map('unserialize', array_values($uniqueArray)); + + $post_data_array['photos'] = $array1; + + $send_post_data = json_encode($post_data_array, JSON_UNESCAPED_UNICODE); + + + /**API시작**/ + $result = $this->syncRequestListNew($hscp_no, $send_post_data); + /**API끝**/ + + + if ($result['code'] == 'success') { + $syncId = ''; + $this->aptModel->saveCheck($rcpt_no, $syncId); + + return $this->response->setJSON([ + 'code' => '0', + 'msg' => 'success' + ]); + + } else { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => '저장실패' + ]); + } + + } catch (\Exception $e) { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => $e->getMessage(), + ]); + } + } + + // 재전송 + public function resendAptInfo() + { + try { + + $rcpt_no = $this->request->getPost('rcpt_no'); + $hscp_no = $this->request->getPost('hscp_no'); + + $params = [ + 'rcpt_no' => $rcpt_no, + 'hscp_no' => $hscp_no, + ]; + + $apiData = $this->aptModel->new_api_photo_send_data($rcpt_no); + + $ip_addr = array( + '172.16.100.2', + '172.16.100.3', + '172.16.100.4', + '172.16.100.5' + ); + + if ( + isset($_SERVER['HTTPS']) && + ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) || + isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && + $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' + ) { + $protocol = 'https://'; + } else { + $protocol = 'http://'; + } + + if (in_array($_SERVER['SERVER_ADDR'], $ip_addr) == false) { + $domain = $protocol . $_SERVER['HTTP_HOST']; + // $domain = $protocol . 'test-admin.confirms.co.kr'; + } else { + $domain = $protocol . 'admin.confirms.co.kr'; + } + + foreach ($apiData as $key => $d) { + $isExposed = ($d['use_yn'] == 'Y' ? true : false); + $array[] = array('mainCategory' => $d['cate1_cd'], 'subCategory' => $d['cate2_cd'], 'comment' => $d['pho_explain']); + $array1[] = array( + 'photoNumber' => $d['pho_no'], + 'mainCategory' => $d['cate1_cd'], + 'subCategory' => $d['cate2_cd'], + 'lat' => $d['pho_lati'], + 'lng' => $d['pho_long'], + 'url' => $d['url'], + 'isExposed' => $isExposed, + 'order' => 1 + ); + } + $serializedArray = array_map('serialize', $array); + $uniqueArray = array_unique($serializedArray); + $post_data_array['categories'] = array_map('unserialize', array_values($uniqueArray)); + + $post_data_array['photos'] = $array1; + $send_post_data = json_encode($post_data_array, JSON_UNESCAPED_UNICODE); + + log_message('debug', 'APT_SEND rcpt_no:::::::' . $rcpt_no . ':::::::' . $send_post_data); + // $send_post_data = to_han( json_encode( $post_data_array) ); + /**API시작**/ + $result = $this->syncRequestListNew($hscp_no, $send_post_data); + /**API끝**/ + + if ($result['code'] == 'success') { + $syncId = ''; + $this->aptModel->saveResend($rcpt_no, $syncId); + + return $this->response->setJSON([ + 'code' => '0', + 'msg' => 'success' + ]); + + } else { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => '저장실패' + ]); + } + + + } catch (\Exception $e) { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => $e->getMessage(), + ]); + } + } + + // 파일업로드 - 동영상, 사진 + public function uploadFile() + { + $lib = new MyUpload(); + + try { + $rcpt_no = $this->request->getPost('rcpt_no'); + $uploadType = $this->request->getPost('upload_type'); + $files = $this->request->getFiles(); + $imgPath = "/upload/apt_file/" . $rcpt_no . "/"; + + $imgPath = NCLOUD_OBJECT_STORAGE_URL . $imgPath; + $moviePath = $imgPath; + $photo360Path = $imgPath; + + if (!isset($files['files'])) { + return $this->response->setJSON([ + 'success' => false, + 'msg' => '파일 없음' + ]); + } + + + if ($uploadType === "photo") { + + + // foreach ($files['files'] as $file) { + // if ($file->isValid()) { + // $file->move(WRITEPATH . 'uploads'); + + // INSERT apt_photo + + print_r($_FILES); + // } + + + + // $imageDataBlob = file_get_contents($object_storage_url); + // $im = new Imagick(); + // $im->readImageBlob($imageDataBlob); + // $im->thumbnailImage(105, 80, false); + // $thumb_im = $im->getImageBlob(); + // $lib->upload_object_storage_imagick(); + + // } + + + } else if ($uploadType === "video") { + + } + + + + } catch (\Exception $e) { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => $e->getMessage(), + ]); + } + } + + + /** + * 동기화요청 API + */ + public function syncRequestListNew($hscp_no, $postData) + { + + $addr = $this->apt_model->getSyncAddr(); + $key = $addr['api_key']; + $server = $addr['api_server']; + $secret = $addr['api_secret']; + $urlString = $server . "/kiso/confirms/complex/" . $hscp_no . "/photo"; + + $postData = str_replace("\\/", '/', $postData); + + + $headers = array(); + $headers[] = 'x-naver-client-id: ' . $key; + if (!empty($secret)) { + $headers[] = 'x-naver-client-secret: ' . $secret; + } + $headers[] = 'Accept: application/json'; + $headers[] = 'Content-Type: application/json'; + + // var_dump( $urlString ); + // var_dump( $headers ); + log_message('debug', ' syncRequestListNew => url =>' . $urlString . '===============postData =>' . $postData); + + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_URL => $urlString, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => '', + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 0, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_POSTFIELDS => $postData, + CURLOPT_HTTPHEADER => $headers, + )); + + $response = curl_exec($curl); + $info = curl_getinfo($curl, CURLINFO_HTTP_CODE); + curl_close($curl); + + $result = json_decode($response, TRUE); + + log_message('debug', ' syncRequestListNew => url =>' . $urlString . '===============returnData =>' . $response . '====http_code===' . $info); + // if ( $info == "200" ) { + if (isset($result) && $result['code'] == 'success') { + return $result; + } else { + return array('code' => 'fail', 'message' => '전송실패', "timeStamp" => date("Y-m-dTH:i:s")); + } + + // $jsonArray = json_decode($response, TRUE); + + // if ($response === false || $info['http_code'] != 200){ + // $return = "false"; + // }else{ + // $jsonArray = json_decode($response, TRUE); + // if ($jsonArray['code'] == "success"){ + // // $return = $jsonArray['syncList']; + // $return = "true"; + + // }else{ + // $return = "false"; + // } + // } + // return $return; + } +} \ No newline at end of file diff --git a/app/Controllers/board/Notice.php b/app/Controllers/Board/Notice.php similarity index 100% rename from app/Controllers/board/Notice.php rename to app/Controllers/Board/Notice.php diff --git a/app/Controllers/listfax/ListFax.php b/app/Controllers/Listfax/ListFax.php similarity index 100% rename from app/Controllers/listfax/ListFax.php rename to app/Controllers/Listfax/ListFax.php diff --git a/app/Controllers/manage/Areas.php b/app/Controllers/Manage/Areas.php similarity index 100% rename from app/Controllers/manage/Areas.php rename to app/Controllers/Manage/Areas.php diff --git a/app/Controllers/manage/Dept.php b/app/Controllers/Manage/Dept.php similarity index 100% rename from app/Controllers/manage/Dept.php rename to app/Controllers/Manage/Dept.php diff --git a/app/Controllers/manage/LoginLog.php b/app/Controllers/Manage/LoginLog.php similarity index 100% rename from app/Controllers/manage/LoginLog.php rename to app/Controllers/Manage/LoginLog.php diff --git a/app/Controllers/manage/Menu.php b/app/Controllers/Manage/Menu.php similarity index 100% rename from app/Controllers/manage/Menu.php rename to app/Controllers/Manage/Menu.php diff --git a/app/Controllers/manage/Permit.php b/app/Controllers/Manage/Permit.php similarity index 100% rename from app/Controllers/manage/Permit.php rename to app/Controllers/Manage/Permit.php diff --git a/app/Controllers/manage/Phone.php b/app/Controllers/Manage/Phone.php similarity index 100% rename from app/Controllers/manage/Phone.php rename to app/Controllers/Manage/Phone.php diff --git a/app/Controllers/manage/Scomplex.php b/app/Controllers/Manage/Scomplex.php similarity index 100% rename from app/Controllers/manage/Scomplex.php rename to app/Controllers/Manage/Scomplex.php diff --git a/app/Controllers/manage/Sms.php b/app/Controllers/Manage/Sms.php similarity index 100% rename from app/Controllers/manage/Sms.php rename to app/Controllers/Manage/Sms.php diff --git a/app/Controllers/manage/User.php b/app/Controllers/Manage/User.php similarity index 100% rename from app/Controllers/manage/User.php rename to app/Controllers/Manage/User.php diff --git a/app/Controllers/results/Assign.php b/app/Controllers/Results/Assign.php similarity index 100% rename from app/Controllers/results/Assign.php rename to app/Controllers/Results/Assign.php diff --git a/app/Controllers/results/Dept.php b/app/Controllers/Results/Dept.php similarity index 100% rename from app/Controllers/results/Dept.php rename to app/Controllers/Results/Dept.php diff --git a/app/Controllers/results/M409.php b/app/Controllers/Results/M409.php similarity index 100% rename from app/Controllers/results/M409.php rename to app/Controllers/Results/M409.php diff --git a/app/Controllers/results/M410.php b/app/Controllers/Results/M410.php similarity index 100% rename from app/Controllers/results/M410.php rename to app/Controllers/Results/M410.php diff --git a/app/Controllers/results/M411.php b/app/Controllers/Results/M411.php similarity index 100% rename from app/Controllers/results/M411.php rename to app/Controllers/Results/M411.php diff --git a/app/Controllers/results/M412.php b/app/Controllers/Results/M412.php similarity index 100% rename from app/Controllers/results/M412.php rename to app/Controllers/Results/M412.php diff --git a/app/Controllers/results/M415.php b/app/Controllers/Results/M415.php similarity index 100% rename from app/Controllers/results/M415.php rename to app/Controllers/Results/M415.php diff --git a/app/Controllers/results/M416.php b/app/Controllers/Results/M416.php similarity index 100% rename from app/Controllers/results/M416.php rename to app/Controllers/Results/M416.php diff --git a/app/Controllers/results/M417.php b/app/Controllers/Results/M417.php similarity index 100% rename from app/Controllers/results/M417.php rename to app/Controllers/Results/M417.php diff --git a/app/Controllers/results/Person.php b/app/Controllers/Results/Person.php similarity index 100% rename from app/Controllers/results/Person.php rename to app/Controllers/Results/Person.php diff --git a/app/Controllers/results/Summary.php b/app/Controllers/Results/Summary.php similarity index 100% rename from app/Controllers/results/Summary.php rename to app/Controllers/Results/Summary.php diff --git a/app/Libraries/MyUpload.php b/app/Libraries/MyUpload.php new file mode 100644 index 0000000..27d33ed --- /dev/null +++ b/app/Libraries/MyUpload.php @@ -0,0 +1,477 @@ +initialize($config); + } + + public function initialize(array $config): self + { + foreach ($config as $k => $v) { + if (property_exists($this, $k)) { + $this->{$k} = $v; + } + } + return $this; + } + + // CI3 스타일: do_upload() + public function do_upload(string $field = 'userfile'): bool + { + $request = service('request'); + $file = $request->getFile($field); + + if (!$file) { + $this->set_error('upload_no_file_selected'); + return false; + } + + if (!$file->isValid()) { + // CI3의 $_FILES 에러코드 대응(가까운 메시지로 매핑) + $err = $file->getError(); + switch ($err) { + case UPLOAD_ERR_INI_SIZE: + $this->set_error('upload_file_exceeds_limit'); + break; + case UPLOAD_ERR_FORM_SIZE: + $this->set_error('upload_file_exceeds_form_limit'); + break; + case UPLOAD_ERR_PARTIAL: + $this->set_error('upload_file_partial'); + break; + case UPLOAD_ERR_NO_FILE: + $this->set_error('upload_no_file_selected'); + break; + case UPLOAD_ERR_NO_TMP_DIR: + $this->set_error('upload_no_temp_directory'); + break; + case UPLOAD_ERR_CANT_WRITE: + $this->set_error('upload_unable_to_write_file'); + break; + case UPLOAD_ERR_EXTENSION: + $this->set_error('upload_stopped_by_extension'); + break; + default: + $this->set_error('upload_no_file_selected'); + break; + } + return false; + } + + // upload_path가 비어있으면 CI3도 실패함 + if (!$this->upload_path) { + $this->set_error('upload_no_file_selected'); + return false; + } + + // (너가 올린 코드처럼) 디렉토리 생성 + $this->make_dirs($this->upload_path); + + // temp file / client file name + $this->file_temp = $file->getTempName(); + $this->client_name = $file->getClientName(); + + // file size (bytes) + $this->file_size = (int) $file->getSize(); // bytes + + // mime / type + $this->file_type = strtolower((string) $file->getClientMimeType()); + $this->file_name = $this->_prep_filename($this->client_name); + $this->file_ext = $this->get_extension($this->file_name); + + // CI3처럼 override 처리 + if ($this->_file_name_override !== '') { + $this->file_name = $this->_prep_filename($this->_file_name_override); + + if (strpos($this->_file_name_override, '.') === false) { + $this->file_name .= $this->file_ext; + } else { + $this->file_ext = $this->get_extension($this->_file_name_override); + } + } + + // allowed type 체크 + if (!$this->is_allowed_filetype($file->getClientExtension(), $this->file_type)) { + $this->set_error('upload_invalid_filetype'); + return false; + } + + // max size 체크 (CI3는 KB 기반) + if (!$this->is_allowed_filesize($this->file_size)) { + $this->set_error('upload_invalid_filesize'); + return false; + } + + // 파일명 정리/길이 제한/공백 제거 + $this->file_name = $this->clean_file_name($this->file_name); + + if ($this->max_filename > 0) { + $this->file_name = $this->limit_filename_length($this->file_name, $this->max_filename); + } + + if ($this->remove_spaces) { + $this->file_name = preg_replace("/\s+/", "_", $this->file_name); + } + + $this->orig_name = $this->file_name; + + // overwrite=false면 충돌 시 파일명 변경(CI3 유사) + if (!$this->overwrite) { + $this->file_name = $this->set_filename($this->upload_path, $this->file_name); + if ($this->file_name === false) + return false; + } + + // (주의) CI3의 do_xss_clean 완전 동일 구현은 어려움 + // 여기서는 위험한 확장자/패턴 정도만 1차 차단 수준으로 처리(원하면 더 강화 가능) + if ($this->xss_clean) { + if (!$this->basic_xss_guard($this->file_name, $this->file_type)) { + $this->set_error('upload_unable_to_write_file'); + return false; + } + } + + // ===== 너의 기존 로직 핵심: 로컬 move 대신 S3 업로드 ===== + $destKeyLikeCi3 = $this->upload_path . $this->file_name; + + if (!$this->upload_object_storage($destKeyLikeCi3, $this->file_temp, 'file')) { + $this->set_error('upload_destination_cloud_error'); + return false; + } + + // 이미지면 width/height + $this->set_image_properties($destKeyLikeCi3, $this->file_temp); + + return true; + } + + // CI3 스타일: data() + public function data(): array + { + $data = [ + 'file_name' => $this->file_name, + 'file_type' => $this->file_type, + 'file_path' => $this->upload_path, + 'full_path' => rtrim($this->upload_path, '/') . '/' . $this->file_name, + 'raw_name' => $this->raw_name($this->file_name), + 'orig_name' => $this->orig_name, + 'client_name' => $this->client_name, + 'file_ext' => $this->file_ext, + 'file_size' => $this->file_size, + 'is_image' => ($this->image_width > 0 && $this->image_height > 0), + 'image_width' => $this->image_width, + 'image_height' => $this->image_height, + 'image_type' => $this->image_type, + 'image_size_str' => $this->image_size_str, + ]; + + if (!empty($this->s3_data)) { + $data = array_merge($data, $this->s3_data); + } + return $data; + } + + public function display_errors(string $open = '', string $close = ''): string + { + if (!$this->errors) + return ''; + return $open . implode($close . $open, $this->errors) . $close; + } + + public function get_errors(): array + { + return $this->errors; + } + + protected function set_error(string $msg): void + { + $this->errors[] = $msg; + } + + // -------------------------------------------------------------------- + // === 너의 기존 S3 메서드들 (CI4용으로 client 생성만 보강) === + + public function upload_object_storage(string $key, string $temp_file, string $type = 'file'): bool + { + // CI3 코드의 경로 치환 로직 유지 (FCPATH는 CI4에도 존재) + $object_storage_upload_path = str_replace(FCPATH, '/', $key); + $object_storage_upload_path = str_replace('/image/confirms_upload/', '/upload/', $object_storage_upload_path); + $object_storage_upload_path = str_replace('//', '/', $object_storage_upload_path); + $object_storage_upload_path = str_replace('/home/www/upload/confirms_upload/', '/upload/', $object_storage_upload_path); + + $s3Client = $this->makeS3Client(); + + try { + $body = file_get_contents($temp_file); + + $response = $s3Client->putObject([ + 'Bucket' => NCLOUD_S3_BUCKET, + 'Key' => ltrim($object_storage_upload_path, '/'), + 'Body' => $body, + 'ACL' => 'public-read', + ]); + + $this->s3_data = [ + 'object_storage_upload_path' => $object_storage_upload_path, + 'object_storage_url' => $response['ObjectURL'] ?? null, + ]; + + } catch (\Throwable $e) { + // 운영에서는 echo 지양. 로그로 남기는 걸 추천 + // log_message('error', $e->getMessage()); + return false; + } + + return true; + } + + public function upload_object_storage_imagick(string $key, $blobData): bool + { + $object_storage_upload_path = str_replace(FCPATH, '/', $key); + $object_storage_upload_path = str_replace('/image/confirms_upload/', '/upload/', $object_storage_upload_path); + $object_storage_upload_path = str_replace('//', '/', $object_storage_upload_path); + $object_storage_upload_path = str_replace('/home/www/upload/confirms_upload/', '/upload/', $object_storage_upload_path); + + $s3Client = $this->makeS3Client(); + + try { + $s3Client->putObject([ + 'Bucket' => NCLOUD_S3_BUCKET, + 'Key' => ltrim($object_storage_upload_path, '/'), + 'Body' => $blobData, + 'ACL' => 'public-read', + ]); + } catch (\Throwable $e) { + return false; + } + return true; + } + + public function upload_object_storage_rename(string $key, string $new_name): bool + { + $s3Client = $this->makeS3Client(); + + try { + $s3Client->copyObject([ + 'Bucket' => NCLOUD_S3_BUCKET, + 'CopySource' => NCLOUD_S3_BUCKET . '/' . ltrim($key, '/'), + 'Key' => ltrim($new_name, '/'), + 'ACL' => 'public-read', + ]); + + $s3Client->deleteObject([ + 'Bucket' => NCLOUD_S3_BUCKET, + 'Key' => ltrim($key, '/'), + ]); + + return true; + } catch (\Throwable $e) { + return false; + } + } + + protected function makeS3Client(): S3Client + { + // AWS SDK v2: S3Client::factory / v3+: new S3Client + if (method_exists(S3Client::class, 'factory')) { + /** @noinspection PhpUndefinedMethodInspection */ + return S3Client::factory([ + 'key' => NCLOUD_S3_KEY, + 'secret' => NCLOUD_S3_SECRET, + 'endpoint' => NCLOUD_S3_ENDPOINT, + 'debug' => true, + 'ssl.certificate_authority' => false, + ]); + } + + return new S3Client([ + 'endpoint' => NCLOUD_S3_ENDPOINT, + 'credentials' => [ + 'key' => NCLOUD_S3_KEY, + 'secret' => NCLOUD_S3_SECRET, + ], + 'region' => defined('NCLOUD_S3_REGION') ? NCLOUD_S3_REGION : 'kr-standard', + 'version' => 'latest', + // (보안주의) 필요하면 verify 설정을 추가 + // 'http' => ['verify' => false], + ]); + } + + // -------------------------------------------------------------------- + // === CI3 Upload 유사 유틸들 === + + protected function is_allowed_filetype(string $ext, string $mime): bool + { + if ($this->allowed_types === '*' || $this->allowed_types === '') + return true; + + $ext = strtolower(ltrim($ext, '.')); + $allowed = array_map('strtolower', explode('|', $this->allowed_types)); + + return in_array($ext, $allowed, true); + } + + protected function is_allowed_filesize(int $bytes): bool + { + if ($this->max_size <= 0) + return true; + $kb = (int) ceil($bytes / 1024); + return $kb <= $this->max_size; + } + + protected function clean_file_name(string $filename): string + { + // CI3처럼 위험문자 제거 + $bad = ['', '<', '>', '"', "'", '&', '$', '=', ';', '?', '/', '\\', '%', '#', '{', '}', '[', ']', ':', ',', '`', '|']; + return str_replace($bad, '', $filename); + } + + protected function limit_filename_length(string $filename, int $length): string + { + if (mb_strlen($filename) <= $length) + return $filename; + $ext = $this->get_extension($filename); + $name = $this->raw_name($filename); + $name = mb_substr($name, 0, max(1, $length - mb_strlen($ext))); + return $name . $ext; + } + + protected function set_filename(string $path, string $filename) + { + $path = rtrim($path, '/') . '/'; + $name = $this->raw_name($filename); + $ext = $this->get_extension($filename); + + $i = 0; + $candidate = $filename; + while (is_file($path . $candidate)) { + $i++; + $candidate = $name . '_' . $i . $ext; + if ($i > 9999) { + $this->set_error('upload_filename_overflow'); + return false; + } + } + return $candidate; + } + + protected function set_image_properties(string $keyLike, string $tempFile): void + { + // 실제 파일은 S3로 올라갔으니 임시파일에서만 추출 + $info = @getimagesize($tempFile); + if (!$info) + return; + + $this->image_width = (int) $info[0]; + $this->image_height = (int) $info[1]; + $this->image_type = (string) ($info['mime'] ?? ''); + $this->image_size_str = (int) ($info[3] ?? 0); + } + + protected function get_extension(string $filename): string + { + $x = strrchr($filename, '.'); + return $x === false ? '' : strtolower($x); + } + + protected function raw_name(string $filename): string + { + $ext = $this->get_extension($filename); + return $ext ? substr($filename, 0, -strlen($ext)) : $filename; + } + + protected function _prep_filename(string $filename): string + { + // 다중 점 처리 등 CI3 유사 + return str_replace(' ', '_', $filename); + } + + protected function basic_xss_guard(string $filename, string $mime): bool + { + // CI3 do_xss_clean 완전판은 아니고 최소 방어 + $lower = strtolower($filename); + if (preg_match('/\.(php|phtml|phar|php\d|cgi|pl|asp|aspx|jsp)$/i', $lower)) { + return false; + } + // mime이 text/html 같은 경우도 거부하고 싶으면 여기서 추가 + return true; + } + + // -------------------------------------------------------------------- + // 너가 올린 make_dirs 로직 이식( VIEW_PATH 없는 부분 보완 ) + protected function make_dirs(string $path, bool $last_is_file = false) + { + $dir_arr = explode("/", $path); + + if (empty($dir_arr[0])) { + $path = ""; + } elseif ($dir_arr[0] === "." || $dir_arr[0] === "..") { + $path = realpath($dir_arr[0]) ?: ''; + $dir_arr[0] = ""; + } else { + $path = "/"; + } + + foreach ($dir_arr as $dir) { + $dir = trim($dir); + if (!$dir) + continue; + + if ($last_is_file) { + if ($dir === $dir_arr[count($dir_arr) - 1]) { + return false; + } + } + + $path = isset($path) ? ($path . "/" . $dir) : $dir; + + if (@is_dir($path)) + continue; + + @mkdir($path, 0707, true); + @chmod($path, 0707); + + // CI3는 VIEW_PATH/index.html 복사였는데 CI4에서는 없을 수 있으니 직접 생성 + $index = rtrim($path, '/') . '/index.html'; + if (!is_file($index)) { + @file_put_contents($index, "403 Forbidden

Directory access is forbidden.

"); + @chmod($index, 0606); + } + } + return true; + } +} diff --git a/app/Models/article/AptModel.php b/app/Models/article/AptModel.php new file mode 100644 index 0000000..47752ca --- /dev/null +++ b/app/Models/article/AptModel.php @@ -0,0 +1,1249 @@ +db->query($sql, [$gugun]); + + } else if (!empty($sido)) { + $chk_sido = substr($sido, '0', '2'); + + if ($chk_sido === '36') { + $sido = substr($sido, '0', '4'); + $sql = "SELECT a.region_cd, TRIM(REPLACE(a.region_nm, b.region_nm, '')) region_nm " . + "FROM region_codes a " . + "LEFT JOIN region_codes b ON b.region_cd = CONCAT(SUBSTR(a.region_cd,1,4),'000000') " . + "WHERE a.region_cd LIKE concat(?, '%') " . + "AND a.region_cd NOT LIKE '%000000' " . + "AND a.region_cd LIKE '%00' " . + "AND a.use_yn = 'Y' " . + "AND EXISTS (SELECT 'x' FROM region_codes c WHERE c.region_cd LIKE CONCAT(SUBSTR(a.region_cd,1,5),'%') AND c.region_cd > CONCAT(SUBSTR(a.region_cd,1,5),'00000')) " . + "ORDER BY a.region_nm ASC"; + } else { + $sido = substr($sido, '0', '2'); + $sql = "SELECT a.region_cd, TRIM(REPLACE(a.region_nm, b.region_nm, '')) region_nm" . + " FROM region_codes a" . + " LEFT JOIN region_codes b ON b.region_cd = CONCAT(SUBSTR(a.region_cd,1,2),'00000000')" . + " WHERE a.region_cd LIKE concat(?, '%')" . + " AND a.region_cd NOT LIKE '%00000000'" . + " AND a.region_cd LIKE '%00000'" . + " AND a.use_yn = 'Y'" . + " AND EXISTS (SELECT 'x' FROM region_codes c WHERE c.region_cd LIKE CONCAT(SUBSTR(a.region_cd,1,5),'%') AND c.region_cd > CONCAT(SUBSTR(a.region_cd,1,5),'00000'))" . + " ORDER BY a.region_nm ASC"; + } + + $query = $this->db->query($sql, [$sido]); + } else { + $sql = "SELECT a.region_cd, a.region_nm " . + "FROM region_codes a " . + "WHERE (a.region_cd LIKE '%00000000' " . + "AND a.use_yn = 'Y') " . + "OR region_cd = 3611000000;"; + + $query = $this->db->query($sql); + } + + + return $query->getResultArray(); + } + + + // 소속본부조회 + public function getBonbuList() + { + $sql = "SELECT dept_sq, pdept_sq, dept_nm, dept_desc, dept_head, use_yn, depth, insert_tm, insert_usr, update_tm, update_usr, lft, rgt" . + " FROM departments" . + " WHERE depth = 1" . + " AND use_yn = 'Y'" . + " ORDER BY lft"; + + $query = $this->db->query($sql); + + + return $query->getResultArray(); + } + + // 소속팀 조회 + public function getTeamList() + { + $sql = "SELECT dept_sq, pdept_sq, dept_nm" . + " FROM departments" . + " WHERE depth = 2" . + " AND use_yn = 'Y'" . + " ORDER BY dept_nm"; + + $query = $this->db->query($sql); + + + return $query->getResultArray(); + } + + // 유저 조회 + public function getUserList() + { + $sql = "SELECT + a.usr_sq, a.usr_id, a.usr_nm, a.dept_sq + FROM users a + WHERE + a.usr_level IN ('3','4','40','5','50','6','60','61','62','7','8','70') + AND a.use_yn = 'Y' + AND EXISTS ( + SELECT 'x' FROM departments a1 INNER JOIN departments a2 ON a2.lft BETWEEN a1.lft AND a1.rgt AND a2.use_yn = 'Y' + WHERE 1=1 AND a2.dept_sq = a.dept_sq AND a1.use_yn = 'Y' + ) + ORDER BY a.usr_level DESC, a.usr_nm ASC "; + + $query = $this->db->query($sql); + + return $query->getResultArray(); + } + + // 조직별 통계 + public function getDeptStatistics($data) + { + $sql = "SELECT + b.dept_sq + , IFNULL(f.dept_nm, '') AS bonbu_nm + , IFNULL(i.dept_nm, '미지정') AS team_nm + , COUNT(b.dept_sq) AS cnt + FROM + apt_receipt a + JOIN + apt_result b ON b.rcpt_no = a.rcpt_no + LEFT JOIN + users c ON b.charger = c.usr_id + LEFT JOIN + region_codes e ON a.region_cd = e.region_cd + LEFT JOIN + departments i ON b.dept_sq = i.dept_sq + LEFT JOIN + departments f ON i.dept_sq = f.dept_sq + WHERE 1=1 "; + + // 사진코드 + if (!empty($data['pho_no'])) { + $sql .= "AND a.rcpt_no IN ( SELECT rcpt_no FROM apt_photo WHERE pho_no = {$data['pho_no']} AND use_yn = 'Y' AND pho_cate1 != 'V') "; + + // 단지코드 + } else if (!empty(($data['hscp_no']))) { + $sql .= "AND a.hasp_no LIKE CONCAT('%', '{$data['hscp_no']}', '%') "; + } else if (!empty($data['rcpt_no'])) { + $sql .= "AND a.rcpt_no = {$data['rcpt_no']}"; + } else { + + // 단지명 + if (!empty($data['rcpt_hscp_nm'])) { + $sql .= "AND a.rcpt_hscp_nm LIKE CONCAT('%', '{$data['rcpt_hscp_nm']}', '%') "; + } + + // 총세대수 + if (!empty($data['households_cnt1'])) { + $sql .= "AND a.households_cnt >= {$data['households_cnt']} "; + } + + if (!empty($data['households_cnt2'])) { + $sql .= "AND a.households_cnt <= {$data['households_cnt2']} "; + } + + + // 법정동코드로 지역구분 + if (!empty($data['srcDong'])) { + $sql .= "AND a.region_cd = '{$data['srcDong']}' "; + } else { + if (!empty($data['srcGugun'])) { + $str_gugun = substr($data['srcGugun'], '0', '2'); + if ($str_gugun == '36') { //세종시는 군구가 없고 바로 동이라서 예외 + $sql .= "AND a.region_cd = '{$data['srcGugun']}' "; + } else { + $gugunPrefix = substr($data['srcGugun'], '0', '5'); + $sql .= "AND a.region_cd LIKE '{$gugunPrefix}%' "; + } + } else { + if (!empty($data['srcSido'])) { + $sidoPrefix = substr($data['srcSido'], '0', '2'); + $sql .= "AND a.region_cd LIKE '{$sidoPrefix}%' "; + } + } + } + + + //촬영일자 == 단지정보작성완료 일자 + if (!empty($data['sdate'])) { + $sql .= "AND b.write_complete_tm >= '{$data['sdate']} 00:00:00' "; + } + + if (!empty($data['edate'])) { + $sql .= "AND b.write_complete_tm <= '{$data['edate']} 00:00:00' "; + } + + // 총동수 + if (!empty($data['dong_cnt1'])) { + $sql .= "AND a.dong_cnt >= {$data['dong_cnt1']} "; + } + + if (!empty($data['dong_cnt2'])) { + $sql .= "AND a.dong_cnt <= {$data['dong_cnt2']} "; + } + + // 진행상태 + if (!empty($data['stat']) && is_array($data['stat'])) { + $statList = "'" . implode("','", $data['stat']) . "'"; + $sql .= " AND b.apt_step IN ({$statList}) "; + } + } + + $sql .= "GROUP BY b.dept_sq + ORDER BY bonbu_nm ASC, team_nm ASC "; + + + $query = $this->db->query($sql); + + return $query->getResultArray(); + } + + // 지역별 통계 + public function getStatistics($data) + { + $sql = "SELECT + a.addr AS addr , COUNT(a.addr) AS cnt + FROM apt_receipt AS a + JOIN apt_result b ON a.rcpt_no = b.rcpt_no + LEFT JOIN users c ON b.charger = c.usr_id + LEFT JOIN region_codes e ON a.region_cd = e.region_cd + LEFT JOIN departments i ON b.dept_sq = i.dept_sq + WHERE 1=1 "; + + // 사진코드 + if (!empty($data['pho_no'])) { + $sql .= "AND a.rcpt_no IN ( SELECT rcpt_no FROM apt_photo WHERE pho_no = {$data['pho_no']} AND use_yn = 'Y' AND pho_cate1 != 'V') "; + + // 단지코드 + } else if (!empty(($data['hscp_no']))) { + $sql .= "AND a.hasp_no LIKE CONCAT('%', '{$data['hscp_no']}', '%') "; + } else if (!empty($data['rcpt_no'])) { + $sql .= "AND a.rcpt_no = {$data['rcpt_no']}"; + } else { + + // 단지명 + if (!empty($data['rcpt_hscp_nm'])) { + $sql .= "AND a.rcpt_hscp_nm LIKE CONCAT('%', '{$data['rcpt_hscp_nm']}', '%') "; + } + + // 총세대수 + if (!empty($data['households_cnt1'])) { + $sql .= "AND a.households_cnt >= {$data['households_cnt']} "; + } + + if (!empty($data['households_cnt2'])) { + $sql .= "AND a.households_cnt <= {$data['households_cnt2']} "; + } + + + // 법정동코드로 지역구분 + if (!empty($data['srcDong'])) { + $sql .= "AND a.region_cd = '{$data['srcDong']}' "; + } else { + if (!empty($data['srcGugun'])) { + $str_gugun = substr($data['srcGugun'], '0', '2'); + if ($str_gugun == '36') { //세종시는 군구가 없고 바로 동이라서 예외 + $sql .= "AND a.region_cd = '{$data['srcGugun']}' "; + } else { + $gugunPrefix = substr($data['srcGugun'], '0', '5'); + $sql .= "AND a.region_cd LIKE '{$gugunPrefix}%' "; + } + } else { + if (!empty($data['srcSido'])) { + $sidoPrefix = substr($data['srcSido'], '0', '2'); + $sql .= "AND a.region_cd LIKE '{$sidoPrefix}%' "; + } + } + } + + + //촬영일자 == 단지정보작성완료 일자 + if (!empty($data['sdate'])) { + $sql .= "AND b.write_complete_tm >= '{$data['sdate']} 00:00:00' "; + } + + if (!empty($data['edate'])) { + $sql .= "AND b.write_complete_tm <= '{$data['edate']} 00:00:00' "; + } + + // 총동수 + if (!empty($data['dong_cnt1'])) { + $sql .= "AND a.dong_cnt >= {$data['dong_cnt1']} "; + } + + if (!empty($data['dong_cnt2'])) { + $sql .= "AND a.dong_cnt <= {$data['dong_cnt2']} "; + } + + // 진행상태 + if (!empty($data['stat']) && is_array($data['stat'])) { + $statList = "'" . implode("','", $data['stat']) . "'"; + $sql .= " AND b.apt_step IN ({$statList}) "; + } + } + + $sql .= "GROUP BY a.addr ORDER BY a.addr ASC "; + + $query = $this->db->query($sql); + + return $query->getResultArray(); + } + + public function getTotalCount($data) + { + $sql = "SELECT + COUNT(*) AS cnt + FROM apt_receipt a + JOIN apt_result b ON b.rcpt_no = a.rcpt_no + LEFT JOIN users c ON b.charger = c.usr_id + LEFT JOIN region_codes e ON a.region_cd = e.region_cd + LEFT JOIN departments i ON b.dept_sq = i.dept_sq + LEFT JOIN codes f ON b.write_complete_yn = f.cd AND f.category = 'PHO_YN' + LEFT JOIN codes h ON b.vdo_up_ynx = h.cd AND h.category = 'VDO_YN' + LEFT JOIN codes g ON b.apt_step = g.cd AND g.category = 'APT_STEP' + LEFT JOIN apt_history j ON a.rcpt_no = j.rcpt_no AND j.changed_detail = 'C2' AND NOT EXISTS (SELECT 'x' FROM apt_history WHERE changed_detail LIKE 'A%' AND rcpt_no = j.rcpt_no) "; + + $sql .= "WHERE 1=1 "; + + // 사진코드 + if (!empty($data['pho_no'])) { + $sql .= "AND a.rcpt_no IN ( SELECT rcpt_no FROM apt_photo WHERE pho_no = {$data['pho_no']} AND use_yn = 'Y' AND pho_cate1 != 'V') "; + + // 단지코드 + } else if (!empty(($data['hscp_no']))) { + $sql .= "AND a.hasp_no LIKE CONCAT('%', '{$data['hscp_no']}', '%') "; + } else if (!empty($data['rcpt_no'])) { + $sql .= "AND a.rcpt_no = {$data['rcpt_no']}"; + } else { + + // 단지명 + if (!empty($data['rcpt_hscp_nm'])) { + $sql .= "AND a.rcpt_hscp_nm LIKE CONCAT('%', '{$data['rcpt_hscp_nm']}', '%') "; + } + + // 총세대수 + if (!empty($data['households_cnt1'])) { + $sql .= "AND a.households_cnt >= {$data['households_cnt']} "; + } + + if (!empty($data['households_cnt2'])) { + $sql .= "AND a.households_cnt <= {$data['households_cnt2']} "; + } + + + // 법정동코드로 지역구분 + if (!empty($data['srcDong'])) { + $sql .= "AND a.region_cd = '{$data['srcDong']}' "; + } else { + if (!empty($data['srcGugun'])) { + $str_gugun = substr($data['srcGugun'], '0', '2'); + if ($str_gugun == '36') { //세종시는 군구가 없고 바로 동이라서 예외 + $sql .= "AND a.region_cd = '{$data['srcGugun']}' "; + } else { + $gugunPrefix = substr($data['srcGugun'], '0', '5'); + $sql .= "AND a.region_cd LIKE '{$gugunPrefix}%' "; + } + } else { + if (!empty($data['srcSido'])) { + $sidoPrefix = substr($data['srcSido'], '0', '2'); + $sql .= "AND a.region_cd LIKE '{$sidoPrefix}%' "; + } + } + } + + + //촬영일자 == 단지정보작성완료 일자 + if (!empty($data['sdate'])) { + $sql .= "AND b.write_complete_tm >= '{$data['sdate']} 00:00:00' "; + } + + if (!empty($data['edate'])) { + $sql .= "AND b.write_complete_tm <= '{$data['edate']} 00:00:00' "; + } + + // 총동수 + if (!empty($data['dong_cnt1'])) { + $sql .= "AND a.dong_cnt >= {$data['dong_cnt1']} "; + } + + if (!empty($data['dong_cnt2'])) { + $sql .= "AND a.dong_cnt <= {$data['dong_cnt2']} "; + } + + // 진행상태 + if (!empty($data['stat']) && is_array($data['stat'])) { + $statList = "'" . implode("','", $data['stat']) . "'"; + $sql .= " AND b.apt_step IN ({$statList}) "; + } + } + + // print ($sql); + + $query = $this->db->query($sql); + + return $query->getRow()->cnt; + } + + // 아파트단지 조회 + public function getAptLists($start, $end, $data) + { + $sql = "SELECT + a.rcpt_no, a.hscp_no, a.addr, a.addr2, a.rcpt_hscp_nm, a.pyeong_cnt, a.households_cnt, a.dong_cnt, a.rcpt_hscp_nm, + a.apt_cate_nm, a.rcpt_x, a.rcpt_y, + b.charger, b.memo, b.write_complete_yn, b.vdo_up_ynx, b.apt_step, b.video_target, b.all_no_pho, + b.dept_sq, b.vdo_up_tm, b.not_vdo_tm, b.check_tm, b.write_complete_tm, + c.usr_nm, i.dept_nm, j.changed_tm + FROM apt_receipt a + JOIN apt_result b ON b.rcpt_no = a.rcpt_no + LEFT JOIN users c ON b.charger = c.usr_id + LEFT JOIN region_codes e ON a.region_cd = e.region_cd + LEFT JOIN departments i ON b.dept_sq = i.dept_sq + LEFT JOIN codes f ON b.write_complete_yn = f.cd AND f.category = 'PHO_YN' + LEFT JOIN codes h ON b.vdo_up_ynx = h.cd AND h.category = 'VDO_YN' + LEFT JOIN codes g ON b.apt_step = g.cd AND g.category = 'APT_STEP' + LEFT JOIN apt_history j ON a.rcpt_no = j.rcpt_no AND j.changed_detail = 'C2' AND NOT EXISTS (SELECT 'x' FROM apt_history WHERE changed_detail LIKE 'A%' AND rcpt_no = j.rcpt_no) "; + + $sql .= "WHERE 1=1 "; + + // 사진코드 + if (!empty($data['pho_no'])) { + $sql .= "AND a.rcpt_no IN ( SELECT rcpt_no FROM apt_photo WHERE pho_no = {$data['pho_no']} AND use_yn = 'Y' AND pho_cate1 != 'V') "; + + // 단지코드 + } else if (!empty(($data['hscp_no']))) { + $sql .= "AND a.hasp_no LIKE CONCAT('%', '{$data['hscp_no']}', '%') "; + } else if (!empty($data['rcpt_no'])) { + $sql .= "AND a.rcpt_no = {$data['rcpt_no']}"; + } else { + + // 단지명 + if (!empty($data['rcpt_hscp_nm'])) { + $sql .= "AND a.rcpt_hscp_nm LIKE CONCAT('%', '{$data['rcpt_hscp_nm']}', '%') "; + } + + // 총세대수 + if (!empty($data['households_cnt1'])) { + $sql .= "AND a.households_cnt >= {$data['households_cnt']} "; + } + + if (!empty($data['households_cnt2'])) { + $sql .= "AND a.households_cnt <= {$data['households_cnt2']} "; + } + + + // 법정동코드로 지역구분 + if (!empty($data['srcDong'])) { + $sql .= "AND a.region_cd = '{$data['srcDong']}' "; + } else { + if (!empty($data['srcGugun'])) { + $str_gugun = substr($data['srcGugun'], '0', '2'); + if ($str_gugun == '36') { //세종시는 군구가 없고 바로 동이라서 예외 + $sql .= "AND a.region_cd = '{$data['srcGugun']}' "; + } else { + $gugunPrefix = substr($data['srcGugun'], '0', '5'); + $sql .= "AND a.region_cd LIKE '{$gugunPrefix}%' "; + } + } else { + if (!empty($data['srcSido'])) { + $sidoPrefix = substr($data['srcSido'], '0', '2'); + $sql .= "AND a.region_cd LIKE '{$sidoPrefix}%' "; + } + } + } + + + //촬영일자 == 단지정보작성완료 일자 + if (!empty($data['sdate'])) { + $sql .= "AND b.write_complete_tm >= '{$data['sdate']} 00:00:00' "; + } + + if (!empty($data['edate'])) { + $sql .= "AND b.write_complete_tm <= '{$data['edate']} 00:00:00' "; + } + + // 총동수 + if (!empty($data['dong_cnt1'])) { + $sql .= "AND a.dong_cnt >= {$data['dong_cnt1']} "; + } + + if (!empty($data['dong_cnt2'])) { + $sql .= "AND a.dong_cnt <= {$data['dong_cnt2']} "; + } + + // 진행상태 + if (!empty($data['stat']) && is_array($data['stat'])) { + $statList = "'" . implode("','", $data['stat']) . "'"; + $sql .= " AND b.apt_step IN ({$statList}) "; + } + } + + + $sql .= "LIMIT {$start}, {$end} "; + + $query = $this->db->query($sql); + + return $query->getResultArray(); + } + + // 전체촬영불가 단지 확인 + public function chkTakeAptPhotoCnt() + { + $sql = "SELECT + COUNT(*) cnt + FROM apt_result + WHERE all_no_pho = 'x' "; + + $query = $this->db->query($sql); + + return $query->getRow()->cnt; + } + + + public function getDeptMapList($dept_sq) + { + $columns = " + b.hscp_no, b.rcpt_hscp_nm, b.rcpt_no, b.dept_sq, b.rcpt_x, b.rcpt_y, + b.charger, i.dept_nm, i.pdept_sq, i.dept_desc, + c.usr_nm, c.usr_id, + (SELECT pdept_sq FROM departments WHERE dept_sq = i.dept_sq) bonbu + "; + + $builder = $this->db->table('apt_receipt a'); + + $builder->select($columns, false); + $builder->join('apt_result b', 'b.rcpt_no = a.rcpt_no'); + $builder->join('users c', 'b.charger = c.usr_id', 'left'); + $builder->join('region_codes e', 'a.region_cd = e.region_cd', 'left'); + $builder->join('departments i', 'b.dept_sq = i.dept_sq', 'left'); + $builder->join('codes f', "b.write_complete_yn = f.cd AND f.category = 'PHO_YN'", 'left'); + $builder->join('codes h', "b.vdo_up_ynx = h.cd AND h.category = 'VDO_YN'", 'left'); + $builder->join('codes g', "b.apt_step = g.cd AND g.category = 'APT_STEP'", 'left'); + + $builder->whereIn('b.dept_sq', $dept_sq); + $builder->orderBy('b.dept_sq', 'ASC'); + + $query = $builder->get(); + + return $query->getResultArray(); + + } + + + // 아파트단지정보저장 + public function saveAptMemo($params) + { + $sql = "UPDATE apt_result SET "; + $sql .= "memo = '{$params['memo']}'"; + if (!empty($params['target'])) { + $sql .= ", video_target = '{$params['target']}'"; + } + + $sql .= "WHERE rcpt_no = {$params['rcpt_no']} "; + + $this->db->query($sql, $params); + + + if ($this->db->transStatus() === false) { + return [ + 'success' => false, + 'msg' => '저장실패', + ]; + } + + // 성공 + return [ + 'success' => true, + ]; + } + + // 아파트 담당자 정보 변경 + public function updateAptDamdang($params) + { + $sql = "UPDATE apt_result SET "; + $sql .= "dept_sq = ?, charger = ? "; + $sql .= "WHERE rcpt_no = ? "; + + $this->db->query($sql, $params); + + + if ($this->db->transStatus() === false) { + return [ + 'success' => false, + 'msg' => '저장실패', + ]; + } + + // 성공 + return [ + 'success' => true, + ]; + } + + // 영상대상 정보 저장 + public function updateAptVideoTarget($params) + { + $sql = "UPDATE apt_result SET "; + $sql .= "video_target = '{$params['target']}' WHERE rcpt_no = {$params['rcpt_no']} "; + + $this->db->query($sql, $params); + + + if ($this->db->transStatus() === false) { + return [ + 'success' => false, + 'msg' => '저장실패', + ]; + } + + if ($params['target'] == 'Y') { + $changed_type = "영상 촬영 대상 지정"; + } else { + $changed_type = "영상 촬영 대상 제외('" . $params['target'] . "')"; + } + + // INSERT apt_history + $this->saveHistory($params['rcpt_no'], $params['apt_step'], 'H', $changed_type, $params['usr_id']); + + // 성공 + return [ + 'success' => true, + ]; + } + + // 엑셀 다운로드 + public function getExcelList($data) + { + $sql = "SELECT + (SELECT cd_nm FROM codes WHERE category = 'APT_STEP' AND cd = b.apt_step) AS '진행상태', + a.hscp_no AS '단지코드', + a.addr AS '주소', + a.addr2 AS '상세주소', + a.rcpt_hscp_nm AS '단지구역명', + a.households_cnt AS '총세대수', + a.dong_cnt AS '총동수', + c.usr_nm AS '방문담당', + b.write_complete_yn AS '사진촬영', + CASE WHEN b.vdo_up_ynx = 'X' THEN '촬영불가' ELSE b.vdo_up_ynx END AS '영상촬영', + (SELECT cd_nm FROM codes WHERE category = 'VIDEO_TARGET' AND cd = b.video_target) AS '영상대상', + b.memo AS '메모', + b.write_complete_tm AS '사진업로드', + b.vdo_up_tm AS '영상업로드', + b.not_vdo_tm AS '촬영불가', + b.check_tm AS '검수', + b.all_no_pho AS '단지 전체 촬영불가' + FROM apt_receipt a + JOIN apt_result b ON b.rcpt_no = a.rcpt_no + LEFT JOIN users c ON b.charger = c.usr_id + LEFT JOIN region_codes e ON a.region_cd = e.region_cd + LEFT JOIN departments i ON b.dept_sq = i.dept_sq + LEFT JOIN codes f ON b.write_complete_yn = f.cd AND f.category = 'PHO_YN' + LEFT JOIN codes h ON b.vdo_up_ynx = h.cd AND h.category = 'VDO_YN' + LEFT JOIN codes g ON b.apt_step = g.cd AND g.category = 'APT_STEP' + LEFT JOIN apt_history j ON a.rcpt_no = j.rcpt_no AND j.changed_detail = 'C2' AND NOT EXISTS (SELECT 'x' FROM apt_history WHERE changed_detail LIKE 'A%' AND rcpt_no = j.rcpt_no) "; + + $sql .= "WHERE 1=1 "; + + // 사진코드 + if (!empty($data['pho_no'])) { + $sql .= "AND a.rcpt_no IN ( SELECT rcpt_no FROM apt_photo WHERE pho_no = {$data['pho_no']} AND use_yn = 'Y' AND pho_cate1 != 'V') "; + + // 단지코드 + } else if (!empty(($data['hscp_no']))) { + $sql .= "AND a.hasp_no LIKE CONCAT('%', '{$data['hscp_no']}', '%') "; + } else if (!empty($data['rcpt_no'])) { + $sql .= "AND a.rcpt_no = {$data['rcpt_no']}"; + } else { + + // 단지명 + if (!empty($data['rcpt_hscp_nm'])) { + $sql .= "AND a.rcpt_hscp_nm LIKE CONCAT('%', '{$data['rcpt_hscp_nm']}', '%') "; + } + + // 총세대수 + if (!empty($data['households_cnt1'])) { + $sql .= "AND a.households_cnt >= {$data['households_cnt']} "; + } + + if (!empty($data['households_cnt2'])) { + $sql .= "AND a.households_cnt <= {$data['households_cnt2']} "; + } + + + // 법정동코드로 지역구분 + if (!empty($data['srcDong'])) { + $sql .= "AND a.region_cd = '{$data['srcDong']}' "; + } else { + if (!empty($data['srcGugun'])) { + $str_gugun = substr($data['srcGugun'], '0', '2'); + if ($str_gugun == '36') { //세종시는 군구가 없고 바로 동이라서 예외 + $sql .= "AND a.region_cd = '{$data['srcGugun']}' "; + } else { + $gugunPrefix = substr($data['srcGugun'], '0', '5'); + $sql .= "AND a.region_cd LIKE '{$gugunPrefix}%' "; + } + } else { + if (!empty($data['srcSido'])) { + $sidoPrefix = substr($data['srcSido'], '0', '2'); + $sql .= "AND a.region_cd LIKE '{$sidoPrefix}%' "; + } + } + } + + + //촬영일자 == 단지정보작성완료 일자 + if (!empty($data['sdate'])) { + $sql .= "AND b.write_complete_tm >= '{$data['sdate']} 00:00:00' "; + } + + if (!empty($data['edate'])) { + $sql .= "AND b.write_complete_tm <= '{$data['edate']} 00:00:00' "; + } + + // 총동수 + if (!empty($data['dong_cnt1'])) { + $sql .= "AND a.dong_cnt >= {$data['dong_cnt1']} "; + } + + if (!empty($data['dong_cnt2'])) { + $sql .= "AND a.dong_cnt <= {$data['dong_cnt2']} "; + } + + // 진행상태 + if (!empty($data['stat']) && is_array($data['stat'])) { + $statList = "'" . implode("','", $data['stat']) . "'"; + $sql .= " AND b.apt_step IN ({$statList}) "; + } + } + + $query = $this->db->query($sql); + + return $query->getResultArray(); + } + + // 이력 저장 + public function saveHistory($rcpt_no, $apt_step, $changed_type, $changed_detail, $charged_id) + { + $sql = "INSERT INTO apt_history" . + " (rcpt_no, apt_step, changed_type, changed_detail, charged_id, changed_tm)" . + " VALUES (?, ?, ?, ?, ?, NOW())"; + $data = [ + $rcpt_no, + $apt_step, + $changed_type, + $changed_detail, + $charged_id + ]; + $res = $this->db->query($sql, $data); + } + + + /** + * 상세페이지 + */ + public function getDetail($rcpt_no) + { + $sql = "SELECT + a.rcpt_no, a.hscp_no, a.addr, a.addr2, a.rcpt_hscp_nm, a.move_ym, a.households_cnt, a.dong_cnt, a.pyeong_cnt, a.apt_cate_nm, a.region_cd, a.rcpt_x, a.rcpt_y, a.pho_exept_yn + ,a.rdate, DATE_FORMAT(a.rdate, '%Y-%m-%d') as rdate_dt ,DATE_FORMAT(a.rdate, '%H:%i:%s') as rdate_tm + ,b.vdo_up_tm, DATE_FORMAT(b.vdo_up_tm, '%Y-%m-%d') as rdate_dt_vdo ,DATE_FORMAT(b.vdo_up_tm, '%H:%i:%s') as rdate_tm_vdo + ,b.not_vdo_tm, DATE_FORMAT(b.not_vdo_tm, '%Y-%m-%d') as rdate_dt_nvdo ,DATE_FORMAT(b.not_vdo_tm, '%H:%i:%s') as rdate_tm_nvdo + ,b.check_tm, DATE_FORMAT(b.check_tm, '%Y-%m-%d') as rdate_dt_chk ,DATE_FORMAT(b.check_tm, '%H:%i:%s') as rdate_tm_chk + ,b.memo, b.note, b.video_target, b.vdo_up_ynx, b.not_vdo_reson, b.apt_step, b.not_vdo_tm, b.check_yn, b.resend_yn, b.write_complete_yn, b.all_no_pho + ,b.write_complete_tm, DATE_FORMAT(b.write_complete_tm, '%Y-%m-%d') as rdate_dt_cmpl ,DATE_FORMAT(b.write_complete_tm, '%H:%i:%s') as rdate_tm_cmpl + ,b.charger, b.dept_sq ,(SELECT pdept_sq FROM departments WHERE dept_sq = b.dept_sq) bonbu + FROM + apt_receipt a + JOIN apt_result b ON a.rcpt_no = b.rcpt_no + + WHERE a.rcpt_no = ?"; + + $query = $this->db->query($sql, [$rcpt_no]); + + return $query->getRowArray(); + } + + // 이미지리스트 + public function getImgList($rcpt_no) + { + $sql = "SELECT pho_no,rcpt_no,pho_cate1,pho_cate2,pho_lati,pho_long,filenm,filenm_up,view_odr,pho_view_yn,pho_date,insert_tm,file_path,use_yn,thumb_path,thumb_nm,cloud_upload_yn" . + " FROM apt_photo" . + " WHERE rcpt_no = ?" . + " AND use_yn = 'Y'"; + + $query = $this->db->query($sql, [$rcpt_no]); + + return $query->getResultArray(); + } + + // 이미지 총 개수 + public function cntAllPho($rcpt_no) + { + $sql = "SELECT COUNT(*) cnt " . + " FROM apt_photo" . + " WHERE rcpt_no= ?" . + " AND use_yn = 'Y'" . + " AND pho_cate1 != 'V'"; + + $query = $this->db->query($sql, [$rcpt_no]); + + return $query->getRow()->cnt; + } + + // 동영상 리스트 + public function getVideoList($rcpt_no) + { + $sql = "SELECT pho_no,rcpt_no,pho_cate1,insert_tm,use_yn,file_path,filenm_up" . + " FROM apt_photo" . + " WHERE rcpt_no = ?" . + " AND pho_cate1 = 'V'" . + " AND use_yn = 'Y'"; + + $query = $this->db->query($sql, [$rcpt_no]); + + return $query->getRowArray(); + } + + // 사진및 설명정보의 카테고리 리스트 + public function getCateInfoList($rcpt_no) + { + $sql = "SELECT rcpt_no, pho_cate2, pho_explain, pho_up_nu" . + " FROM apt_category" . + " WHERE rcpt_no = ?"; + + $query = $this->db->query($sql, [$rcpt_no]); + + return $query->getResultArray(); + } + + // 정보변경이력 + public function getHistory($rcpt_no) + { + $sql = " SELECT seq," . + " rcpt_no, " . + " apt_step, get_code_name('APT_STEP',apt_step) AS apt_step_nm, " . + " changed_type, get_code_name('APT_CHANGED_TYPE',changed_type) AS changed_type_nm, " . + " changed_detail, get_code_name('APT_CHANGED_DETAIL',changed_detail) AS changed_detail_nm, " . + " charged_id, " . + " changed_tm, DATE_FORMAT(changed_tm, '%Y-%m-%d') as rdate_dt, DATE_FORMAT(changed_tm, '%H:%i:%s') as rdate_tm" . + " FROM apt_history" . + " WHERE rcpt_no = ?" . + " ORDER BY changed_tm DESC"; + + $query = $this->db->query($sql, [$rcpt_no]); + + return $query->getResultArray(); + } + + // 사진 카테고리 조회 + public function getCateJson($params) + { + $sql = "SELECT cd, cd_nm" . + " FROM codes" . + " WHERE cd LIKE CONCAT(?, '%')" . + " AND category = 'PHO_CATE2'"; + + $query = $this->db->query($sql, [$params]); + + + return $query->getResultArray(); + } + + // 담당자정보저장 + public function saveKeeper($params) + { + $sql = "UPDATE apt_result SET + dept_sq = {$params['team']}, charger = '{$params['user']}' + WHERE rcpt_no = {$params['rcpt_no']} + "; + + + if ($this->db->query($sql) === false) { + return [ + 'success' => false, + 'msg' => '저장실패', + ]; + } + + // 성공 + return [ + 'success' => true, + ]; + } + + // 지도 좌표저장 + public function saveCoordinate($params) + { + $sql = "UPDATE apt_receipt SET + rcpt_x = '{$params['rcpt_x']}', + rcpt_y = '{$params['rcpt_y']}' + WHERE rcpt_no = {$params['rcpt_no']}"; + + if ($this->db->query($sql) === false) { + return [ + 'success' => false, + 'msg' => 'apt_receipt 저장 실패', + ]; + } + + $sql = "UPDATE apt_result SET + rcpt_x = '{$params['rcpt_x']}', + rcpt_y = '{$params['rcpt_y']}' + WHERE rcpt_no = {$params['rcpt_no']}"; + + if ($this->db->query($sql) === false) { + return [ + 'success' => false, + 'msg' => 'apt_result 저장 실패', + ]; + } + + return [ + 'success' => true, + ]; + } + + // 단지 특이사항 저장 + public function saveNote($params) + { + $sql = "UPDATE apt_result SET + note = '{$params['note']}' + WHERE rcpt_no = {$params['rcpt_no']}"; + + if ($this->db->query($sql) === false) { + return [ + 'success' => false, + 'msg' => '저장 실패', + ]; + } + + return [ + 'success' => true, + ]; + } + + // 동영상 촬영정보 저장 + public function saveVideoTarget($params) + { + $sql = "UPDATE apt_result SET + video_target = '{$params['target']}' + WHERE rcpt_no = {$params['rcpt_no']} + "; + + + if ($this->db->query($sql) === false) { + return [ + 'success' => false, + 'msg' => '저장 실패', + ]; + } + + $now = $this->getDetail($params['rcpt_no']); + if ($params['target'] == 'Y') { + $changed_type = "영상 촬영 대상 지정"; + } else { + $changed_type = "영상 촬영 대상 제외('" . $params['target'] . "')"; + } + + $this->saveHistory($params['rcpt_no'], $now['apt_step'], 'H', $changed_type, session('usr_id')); + + return [ + 'success' => true, + ]; + } + + // 촬영불가사유 저장 + public function saveVideoReason($params) + { + $sql = "UPDATE apt_result SET + not_vdo_reason = '{$params['not_vdo_reason']}' + , vdo_up_ynx = '{$params['vdo_up_ynx']}' + , not_vdo_tm = NOW() + , apt_step = CASE WHEN write_complete_yn = 'Y' THEN 'S04' ELSE 'S03' END + WHERE rcpt_no = {$params['rcpt_no']} + "; + + if ($this->db->query($sql) === false) { + return [ + 'success' => false, + 'msg' => '저장 실패', + ]; + } + + $now = $this->getDetail($params['rcpt_no']); + $this->saveHistory($params['rcpt_no'], $now['apt_step'], 'G', 'G1', session('usr_id')); + + + return [ + 'success' => true, + ]; + } + + // 사진업로드 불가사유 저장 + public function savePhoReason($params) + { + $sql = "INSERT INTO apt_category + (rcpt_no, pho_cate2, pho_up_nu, insert_tm, insert_tm, insert_user_id) + VALUES + ({$params['rcpt_no']}, '{$params['pho_cate2']}', '{$params['pho_up_nu']}', NOW(), {$params['usr_id']}) + "; + + if ($this->db->query($sql) === false) { + return [ + 'success' => false, + 'msg' => '저장 실패', + ]; + } + + return [ + 'success' => true, + ]; + } + + + // 사진 카테고리 수정 + public function saveCate($params) + { + + $data = [ + 'pho_cate1' => $params['pho_cate1'], + 'pho_cate2' => $params['pho_cate2'], + ]; + + $this->db->where_in('pho_no', $params['pho_no']); + $result = $this->db->update('apt_photo', $data); + + if ($result === false) { + return [ + 'success' => false, + 'msg' => 'DB 업데이트 실패', + ]; + } + + $sql2 = " DELETE FROM apt_category" . //2.조건에맞는거 = 해당카테고리에 사진이 없을때 삭제 + " WHERE rcpt_no = {$params['rcpt_no']}" . + " AND pho_cate2 = '{$params['nowCate2']}'" . + " AND NOT EXISTS ( SELECT 'x' FROM apt_photo b" . + " WHERE b.rcpt_no = apt_category.rcpt_no" . + " AND b.pho_cate2 = apt_category.pho_cate2" . + " AND b.use_yn = 'Y')"; + + if ($this->db->query($sql2) === false) { + return [ + 'success' => false, + 'msg' => '처리 실패', + ]; + } + + $sql3 = " UPDATE apt_category" . //3. 이동할 카테고리에 이미 불가사유가 있다면 불가사유 null로 업데이트 + " SET pho_up_nu = NULL" . + " WHERE rcpt_no = {$params['rcpt_no']}" . + " AND pho_cate2 = '{$params['pho_cate2']}'" . + " AND pho_up_nu IS NOT NULL"; + + + if ($this->db->query($sql3) === false) { + return [ + 'success' => false, + 'msg' => '처리 실패', + ]; + } + + $now = $this->getDetail($params['rcpt_no']); + $this->saveHistory($params['rcpt_no'], $now['apt_step'], 'D', 'D1', session('usr_id')); + + return [ + 'success' => true, + ]; + } + + // 사진 노출여부 수정 + public function savePhotoView($params) + { + $data = [ + 'pho_view_yn' => $params['pho_view_yn'], + ]; + + $builder = $this->db->table('apt_photo'); + + $builder->whereIn('pho_no', $params['pho_no']); + // $builder->where('rcpt_no', $params['rcpt_no']); + + if (!$builder->update($data)) { + return [ + 'success' => false, + 'msg' => '처리 실패', + ]; + } + + $now = $this->getDetail($params['rcpt_no']); + if ($params['pho_view_yn'] === 'Y') {//노출시작 + $this->saveHistory($params['rcpt_no'], $now['apt_step'], 'E', 'E1', session('usr_id')); + } else {//종료 + $this->saveHistory($params['rcpt_no'], $now['apt_step'], 'E', 'E2', session('usr_id')); + } + + return [ + 'success' => true, + ]; + } + + // 사진정보삭제 + public function removePhoto($params) + { + $data = [ + 'use_yn' => 'N', + ]; + + $builder = $this->db->table('apt_photo'); + + $builder->whereIn('pho_no', $params['pho_no']); + $builder->where('rcpt_no', $params['rcpt_no']); + + if (!$builder->update()) { + return [ + 'success' => false, + 'msg' => '처리 실패', + ]; + } + + $sql2 = "SELECT pho_cate2 FROM apt_photo + WHERE rcpt_no = {$params['rcpt_no']} + AND pho_cate = '{$params['cate2_cd']}' + AND use_yn = 'Y' + "; + + $query2 = $this->db->query($sql2); + + if ($this->db->transStatus() == false) { + return [ + 'success' => false, + 'msg' => '처리 실패', + ]; + } + + if ($query2->getNumRows() > 0) { + $sql3 = "DELETE FROM apt_category + WHERE rcpt_no = {$params['rcpt_no']} + AND pho_cate2 = '{$params['cate2_cd']}' + "; + + $query3 = $this->db->query($sql3); + } + + return [ + 'success' => true, + ]; + } + + + // 사진설명 저장 + public function savePhoExplain($params) + { + $sql = "INSERT INTO apt_category + (rcpt_no, pho_cate2, pho_explain, insert_tm, insert_user_id) + VALUES + ({$params['rcpt_no']}, '{$params['pho_cate2']}', '{$params['pho_explain']}', NOW(), '{$params['usr_id']}') + "; + + if ($this->db->query($sql) === false) { + return [ + 'success' => false, + 'msg' => '저장실패', + ]; + } + + $now = $this->getDetail($params['rcpt_no']); + $this->saveHistory($params['rcpt_no'], $now['apt_step'], 'D', '사진설명 작성', $params['usr_id']); + + // 성공 + return [ + 'success' => true, + ]; + } + + + // 단지실사 API 정보 + public function new_api_photo_send_data($rcpt_no) + { + $sql = "SELECT c2.cd_nm as cate1_nm , c1.cd_nm cate2_nm , c1.cd_naver cate2_cd , c2.cd_naver cate1_cd , ap.pho_no , ap.pho_view_yn FROM apt_category ac + LEFT JOIN codes c1 ON ac.pho_cate2 = c1.cd AND c1.category = 'PHO_CATE2' + LEFT JOIN codes c2 ON LEFT( c1.cd_naver , 2) = LEFT ( c2.cd_naver , 2) AND c2.category = 'PHO_CATE1' + LEFT JOIN apt_photo ap ON c1.cd = ap.pho_cate2 AND ap.rcpt_no = ac.rcpt_no + WHERE ac.rcpt_no = ? + AND pho_no IS NOT NULL + group by c1.cd_naver "; + + $query = $this->db->query($sql, [$rcpt_no]); + + return $query->getResultArray(); + } + + // 검수완료 + public function saveCheck($rcpt_no, $syncId) + { + $sql = "UPDATE apt_result" . + " SET check_yn = 'Y'" . + " ,check_tm = NOW()" . + " ,apt_step = 'S05'" . + " ,syncid = ?" . + " WHERE rcpt_no = ? "; + + if ($this->db->query($sql, [$syncId, $rcpt_no]) === false) { + return [ + 'success' => false, + 'msg' => '저장실패', + ]; + } + + $this->saveHistory($rcpt_no, 'S05', 'I', 'I1', session('usr_id')); + + return [ + 'success' => true, + ]; + } + + // 재전송 + public function saveResend($rcpt_no, $syncId) + { + $sql = " UPDATE apt_result" . + " SET resend_yn = 'Y'" . + " ,check_tm = NOW()" . + " ,syncid = ?" . + " WHERE rcpt_no = ? "; + + + if ($this->db->query($sql, [$syncId, $rcpt_no]) === false) { + return [ + 'success' => false, + 'msg' => '저장실패', + ]; + } + + $this->saveHistory($rcpt_no, 'S05', 'J', 'J1', session('usr_id')); + + return [ + 'success' => true, + ]; + } +} \ No newline at end of file diff --git a/app/Models/common/CodeModel.php b/app/Models/common/CodeModel.php index b273109..9fdcdce 100644 --- a/app/Models/common/CodeModel.php +++ b/app/Models/common/CodeModel.php @@ -14,9 +14,44 @@ class CodeModel extends Model " WHERE category = ?" . " AND use_yn = 'Y'" . " ORDER BY view_odr"; - $data = array($category); + $data = [$category]; $query = $this->db->query($sql, $data); return $query->getResultArray(); } + + + public function getCodeLists($data): array + { + return $this->db->table('codes') + ->select('category, category_nm, cd, cd_nm') + ->whereIn('category', $data) + ->where('use_yn', 'Y') + ->orderBy('view_odr') + ->get() + ->getResultArray(); + } + + public function getCategoryCodeList($category = array(), $useYn = '') + { + $this->db->select('category, cd, cd_nm, use_yn'); + $this->db->from('codes'); + $this->db->where_in('category', $category); + if (!empty($useYn)) { + $this->db->where('use_yn', $useYn); + } + $this->db->order_by('category', 'asc'); + $this->db->order_by('view_odr', 'asc'); + + $query = $this->db->get(); + + //echo $this->db->last_query()."
"; + + //여기 아래부분을 해줘야 배열을 카테고리로 뽑아쓸수있다 위에는 배열에 배열이담김 + $codes = []; + foreach ($query->getResultArray() as $row) { + $codes[$row['category']][] = ['cd' => $row['cd'], 'cd_nm' => $row['cd_nm']]; + } + return $codes; + } } \ No newline at end of file diff --git a/app/Views/pages/article/detail.php b/app/Views/pages/article/detail.php new file mode 100644 index 0000000..1486799 --- /dev/null +++ b/app/Views/pages/article/detail.php @@ -0,0 +1,1614 @@ +extend('layouts/main') ?> + +section('content') ?> + + +

아파트단지 DB구축 상세

+ +
+
+ +
+
+
단지 정보
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
단지코드주소
단지명상세주소
입주년월총세대수
총동수메모 +
+
+ + +
+
+ +
+
+
+
+
+ +
+
+
담당자 정보
+ + + + + + + + + + + + + + + + + + + + + + +
관할본부 + + 담당팀 + + 담당자 + + + +
+
+
+ +
+
+
단지 상태 정보
+ + + + + + + + + + + + + + + + + + + + + + + + +
상태내역대상업로드사진업로드영상업로드영상촬영불가검수
+
+
+ + +
+ +
+
+
+
+
+
+
+
+
+
+ +
+
+
단지 위치
+ + + + + + + + + + + + + + + + + +
+
+
+
단지 특이사항
+ + + +
+
+ +
+
+
지도 좌표 수정 +
+ 위도 + + + 경도 + + + +
+
+
+
+ +
+
+
단지 영상 정보
+ + + + + + + + + + + + + + + + +
동영상 + 비디오
+ +
동영상 촬영불가 + + 영상촬영대상 +
+
+ +
+
+ +
+
+
촬영불가사유 +
+
+ +
+
+ +
+
+
+
+
+ +
+
+
단지 사진 기본 설정
+ + + + + + + + + + + + + + + + + +
사진 일괄업로드 + + 사진 일괄삭제 + + 단지정보 작성완료 + +
+
+
+ +
+
+
단지 사진 및 설명 정보
+ + + + + + + + + + + + + + $val) { + if (($val['pho_cate2'] ?? '') === 'V') + continue; + if ((string) ($val['pho_cate2'] ?? '') === $c2cd) { + $arrCate2Image[] = $val; + unset($image[$key]); // 원본 유지 + } + } + } + + // ✅ 업로드불가 사유(cateInfo) + $arrCate2NU = []; + if (!empty($cateInfo)) { + foreach ($cateInfo as $val) { + if (($val['pho_cate2'] ?? '') === 'V') + continue; + if ((string) ($val['pho_cate2'] ?? '') === $c2cd) { + $arrCate2NU[] = $val; + } + } + } + + // ✅ 설명 텍스트는 미리 뽑아서 textarea에 그대로 출력(포매터 안정) + $phoExplain = ''; + if (!empty($cateInfo)) { + foreach ($cateInfo as $row) { + if ((string) ($row['pho_cate2'] ?? '') === $c2cd) { + $phoExplain = (string) ($row['pho_explain'] ?? ''); + break; + } + } + } + + // ✅ 오른쪽 버튼 영역에서 사용할 rcpt_no (원본은 $img 마지막 값 의존이라 위험) + $rcptNoForActions = (string) ($apt['rcpt_no'] ?? ''); + if (!empty($arrCate2Image) && !empty($arrCate2Image[0]['rcpt_no'])) { + $rcptNoForActions = (string) $arrCate2Image[0]['rcpt_no']; + } + ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + +
+ + + +
+ + + + + + + + +   사진업로드 불가사유    + +   + +     + + + + + + + +   사진업로드 불가사유    + +   + +     + + + + + + +
+ + + + + + + + + + + + + + + + + + +
선택된 사진을
+   + +   + + +
+ + + + + +
+ +
+
+ + + + + + + + + + + + + +
+ + + + +
+ +
+
+ + +
+ + + +
+
+ +
+
+
정보변경 이력
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
진행상태변경내용처리자(ID)처리일시세부내용
+
+
+
+
+ + +section('modals') ?> + + + +endSection() ?> + + + + + + + +endSection() ?> \ No newline at end of file diff --git a/app/Views/pages/article/lists.php b/app/Views/pages/article/lists.php new file mode 100644 index 0000000..9b15d45 --- /dev/null +++ b/app/Views/pages/article/lists.php @@ -0,0 +1,1302 @@ +extend('layouts/main') ?> + +section('content') ?> + + +

아파트단지 DB구축 현황

+ +
+
+
+ +
+
+
+ + + + + +
+
+ +
+ + 단지코드/사진코드를 입력하면 다른 조건은 무시됩니다. + +
+ +
+ +
+ + +
+ + +
+ + +
+ + +
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+ + +
+
+
+ +
+ +
+ + ~ + +
+
+ + +
+ +
+ + ~ + +
+
+ + +
+ +
+ + ~ + +
+
+ + +
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+ +
+
+ + +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ + +
+ +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + +
+ + 관할본부방문담당배정건수
+
+
+ + +
+
+ +
+
+
+
+ + + + + + + + +
방문담당배정건수
+
+
+
+
+
+ + +
+
+ +
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + | + + 영상대상 + + + + +
+ +
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ + 진행상태단지코드주소상세주소단지구역명총세대수총동수방문팀방문담당사진촬영영상촬영영상대상메모저장
+
+
+
+ + +section('modals') ?> + +endSection() ?> + + + + + +endSection() ?> \ No newline at end of file diff --git a/app/Views/pages/article/printMap.php b/app/Views/pages/article/printMap.php new file mode 100644 index 0000000..7ff91cc --- /dev/null +++ b/app/Views/pages/article/printMap.php @@ -0,0 +1,237 @@ + + + + + + + + + 컨펌스 로그인 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ 관할 배정 지도 +
+
+ +
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/composer.json b/composer.json index 38a51e2..017baa8 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,7 @@ }, "require": { "php": "^8.1", + "aws/aws-sdk-php": "^3.369", "codeigniter4/framework": "^4.0" }, "require-dev": { diff --git a/composer.lock b/composer.lock index de76cfa..dae606f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,159 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "657ac079313fab046eb8c29fe1d6992b", + "content-hash": "f89d196f2c18e8021010c94fe29c716a", "packages": [ + { + "name": "aws/aws-crt-php", + "version": "v1.2.7", + "source": { + "type": "git", + "url": "https://github.com/awslabs/aws-crt-php.git", + "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/d71d9906c7bb63a28295447ba12e74723bd3730e", + "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35||^5.6.3||^9.5", + "yoast/phpunit-polyfills": "^1.0" + }, + "suggest": { + "ext-awscrt": "Make sure you install awscrt native extension to use any of the functionality." + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "AWS SDK Common Runtime Team", + "email": "aws-sdk-common-runtime@amazon.com" + } + ], + "description": "AWS Common Runtime for PHP", + "homepage": "https://github.com/awslabs/aws-crt-php", + "keywords": [ + "amazon", + "aws", + "crt", + "sdk" + ], + "support": { + "issues": "https://github.com/awslabs/aws-crt-php/issues", + "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.7" + }, + "time": "2024-10-18T22:15:13+00:00" + }, + { + "name": "aws/aws-sdk-php", + "version": "3.369.1", + "source": { + "type": "git", + "url": "https://github.com/aws/aws-sdk-php.git", + "reference": "b2d04cf1184a96839a8ab62ec6e3cf2d62a278e3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/b2d04cf1184a96839a8ab62ec6e3cf2d62a278e3", + "reference": "b2d04cf1184a96839a8ab62ec6e3cf2d62a278e3", + "shasum": "" + }, + "require": { + "aws/aws-crt-php": "^1.2.3", + "ext-json": "*", + "ext-pcre": "*", + "ext-simplexml": "*", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/promises": "^2.0", + "guzzlehttp/psr7": "^2.4.5", + "mtdowling/jmespath.php": "^2.8.0", + "php": ">=8.1", + "psr/http-message": "^1.0 || ^2.0", + "symfony/filesystem": "^v6.4.3 || ^v7.1.0 || ^v8.0.0" + }, + "require-dev": { + "andrewsville/php-token-reflection": "^1.4", + "aws/aws-php-sns-message-validator": "~1.0", + "behat/behat": "~3.0", + "composer/composer": "^2.7.8", + "dms/phpunit-arraysubset-asserts": "^0.4.0", + "doctrine/cache": "~1.4", + "ext-dom": "*", + "ext-openssl": "*", + "ext-sockets": "*", + "phpunit/phpunit": "^9.6", + "psr/cache": "^2.0 || ^3.0", + "psr/simple-cache": "^2.0 || ^3.0", + "sebastian/comparator": "^1.2.3 || ^4.0 || ^5.0", + "yoast/phpunit-polyfills": "^2.0" + }, + "suggest": { + "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", + "doctrine/cache": "To use the DoctrineCacheAdapter", + "ext-curl": "To send requests using cURL", + "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", + "ext-pcntl": "To use client-side monitoring", + "ext-sockets": "To use client-side monitoring" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Aws\\": "src/" + }, + "exclude-from-classmap": [ + "src/data/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Amazon Web Services", + "homepage": "http://aws.amazon.com" + } + ], + "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", + "homepage": "http://aws.amazon.com/sdkforphp", + "keywords": [ + "amazon", + "aws", + "cloud", + "dynamodb", + "ec2", + "glacier", + "s3", + "sdk" + ], + "support": { + "forum": "https://github.com/aws/aws-sdk-php/discussions", + "issues": "https://github.com/aws/aws-sdk-php/issues", + "source": "https://github.com/aws/aws-sdk-php/tree/3.369.1" + }, + "time": "2025-12-22T19:13:21+00:00" + }, { "name": "codeigniter4/framework", "version": "v4.6.3", @@ -80,6 +231,331 @@ }, "time": "2025-08-02T13:36:13+00:00" }, + { + "name": "guzzlehttp/guzzle", + "version": "7.10.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2025-08-23T22:36:01+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "481557b130ef3790cf82b713667b43030dc9c957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.3.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2025-08-22T14:34:08+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.8.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "21dc724a0583619cd1652f673303492272778051" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.8.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2025-08-23T21:21:41+00:00" + }, { "name": "laminas/laminas-escaper", "version": "2.18.0", @@ -141,6 +617,232 @@ ], "time": "2025-10-14T18:31:13+00:00" }, + { + "name": "mtdowling/jmespath.php", + "version": "2.8.0", + "source": { + "type": "git", + "url": "https://github.com/jmespath/jmespath.php.git", + "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/a2a865e05d5f420b50cc2f85bb78d565db12a6bc", + "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-mbstring": "^1.17" + }, + "require-dev": { + "composer/xdebug-handler": "^3.0.3", + "phpunit/phpunit": "^8.5.33" + }, + "bin": [ + "bin/jp.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "files": [ + "src/JmesPath.php" + ], + "psr-4": { + "JmesPath\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Declaratively specify how to extract elements from a JSON document", + "keywords": [ + "json", + "jsonpath" + ], + "support": { + "issues": "https://github.com/jmespath/jmespath.php/issues", + "source": "https://github.com/jmespath/jmespath.php/tree/2.8.0" + }, + "time": "2024-09-04T18:46:31+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, { "name": "psr/log", "version": "3.0.2", @@ -190,6 +892,355 @@ "source": "https://github.com/php-fig/log/tree/3.0.2" }, "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "d551b38811096d0be9c4691d406991b47c0c630a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/d551b38811096d0be9c4691d406991b47c0c630a", + "reference": "d551b38811096d0be9c4691d406991b47c0c630a", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-11-27T13:27:24+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-23T08:48:59+00:00" } ], "packages-dev": [ @@ -1980,73 +3031,6 @@ ], "time": "2023-02-07T11:34:05+00:00" }, - { - "name": "symfony/deprecation-contracts", - "version": "v3.6.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-25T14:21:43+00:00" - }, { "name": "theseer/tokenizer", "version": "1.3.1", diff --git a/public/plugin/img/photo.gif b/public/plugin/img/photo.gif new file mode 100644 index 0000000..2807052 Binary files /dev/null and b/public/plugin/img/photo.gif differ diff --git a/public/plugin/img/pin.png b/public/plugin/img/pin.png new file mode 100644 index 0000000..05fa15c Binary files /dev/null and b/public/plugin/img/pin.png differ