변경 파일 목록
총 76개
2026-10-06 · 문자 발신번호 관리 프로세스 개선.
layer_manage_call_number.phpAdmin/member/sms_auto.phpAdmin/member/sms_send.phpAdmin/member/admin_log_list.phpAdmin/policy/manage_list.phpAdmin/policy/_manage_permission_manager_list.phpAdmin/policy/_manage_permission_menu_list.phpAdmin/policy/manage_permission.phpAdmin/policy/manage_register.phpAdmin/policy/manage_security.phpAdmin/policy/cart_remind_list.phpAdmin/promotion/comeback_coupon_list.phpAdmin/promotion/layer_message_representative_register_guide.phpAdmin/share/layer_sms_personal_info_access_log.phpAdmin/share/manager_certification_result.phpAdmin/share/AdminLogDAO.phpComponent/Admin/AdminMenu.phpComponent/Admin/CartRemind.phpComponent/CartRemind/CouponAdmin.phpComponent/Coupon/DBTableField.phpComponent/Database/ManagerCs.phpComponent/Member/Manager.phpComponent/Member/SmsAdmin.phpComponent/Sms/SmsAuto.phpComponent/Sms/SmsSender.phpComponent/Sms/LayerSmsAuthController.phpController/Admin/Base/LoginPsController.phpController/Admin/Base/SmsAutoController.phpController/Admin/Member/SmsPsController.phpController/Admin/Member/SmsSendController.phpController/Admin/Member/AdminLogListController.phpController/Admin/Policy/ManageListController.phpController/Admin/Policy/ManagePermissionController.phpController/Admin/Policy/ManagePsController.phpController/Admin/Policy/ManageRegisterController.phpController/Admin/Policy/ManageSecurityController.phpController/Admin/Policy/CartRemindListController.phpController/Admin/Promotion/ComebackCouponListController.phpController/Admin/Promotion/LayerExcelAuthController.phpController/Admin/Share/LayerManagePermissionController.phpController/Admin/Share/2026-10-26 · SMS 발송 보안 강화
layer_sms_template_approval_history.phpAdmin/member/layer_sms_template_auto_send.phpAdmin/member/layer_sms_template_form.phpAdmin/member/layer_sms_template_load.phpAdmin/member/layer_sms_template_preview.phpAdmin/member/sms_auto.phpAdmin/member/_sms_replace_code_table.phpAdmin/member/sms_send.phpAdmin/member/sms_template.phpAdmin/member/_manage_permission_menu_list.phpAdmin/policy/manage_permission.phpAdmin/policy/manage_register.phpAdmin/policy/manage_security.phpAdmin/policy/cart_remind_list.phpAdmin/promotion/comeback_coupon_list.phpAdmin/promotion/layer_message_representative_register_guide.phpAdmin/share/layer_message_representative_send_restrict.phpAdmin/share/layer_occupancy_auth.phpAdmin/share/layer_sms_contents.phpAdmin/share/AdminMenu.phpComponent/Admin/DBTableField.phpComponent/Database/Manager.phpComponent/Member/SmsAdmin.phpComponent/Sms/SmsAuto.phpComponent/Sms/SmsAutoController.phpController/Admin/Member/SmsSendController.phpController/Admin/Member/SmsSendPsController.phpController/Admin/Member/ManagePsController.phpController/Admin/Policy/ManageRegisterController.phpController/Admin/Policy/CartRemindListController.phpController/Admin/Promotion/CartRemindPsController.phpController/Admin/Promotion/ComebackCouponListController.phpController/Admin/Promotion/CouponPsController.phpController/Admin/Promotion/LayerManagePermissionController.phpController/Admin/Share/LayerSmsContentsController.phpController/Admin/Share/LayerSmsPasswordPsController.phpController/Admin/Share/변경 파일 코드
추가삭제
변경 전layer_manage_call_number.php
변경 후layer_manage_call_number.php
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script> | |
| <style> | |
| /* 어드민 공용 CSS 가 라디오를 배경 이미지로 대체해 :disabled 의 grayscale 이 드러나지 않으므로 이 레이어에서만 비활성 표시를 덧붙인다 */ | |
| #layerManageCallNumber input[type="radio"]:disabled { | |
| opacity: 0.4; | |
| cursor: not-allowed; | |
| } | |
| </style> | |
| <div id="layerManageCallNumber"> | |
| <table class="table table-rows table-fixed"> | |
| <colgroup> | |
| <col class="width10p"/> | |
| <col class="width30p"/> | |
| <col class="width20p"/> | |
| <col class="width10p"/> | |
| <col class="width20p"/> | |
| <col class="width10p"/> | |
| </colgroup> | |
| <thead> | |
| <tr> | |
| <th>선택</th> | |
| <th>발신번호</th> | |
| <th>관리메모</th> | |
| <th>상태</th> | |
| <th>승인일</th> | |
| <th>관리</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <?php | |
| if (!empty($smsCallNumberList)) { | |
| foreach ($smsCallNumberList as $key => $smsCallNumber) { | |
| ?> | |
| <tr class="text-center"> | |
| <td class="form-inline"> | |
| <?php if ($currentCallNumber !== '' && $currentCallNumber === $smsCallNumber->getCallNumber()) { ?> | |
| 사용 중 | |
| <?php } else { ?> | |
| <input type="radio" name="selectNum" id="selectNum<?= $key ?>" value="<?= $smsCallNumber->getCallNumber() ?>" text="<?= gd_number_to_phone($smsCallNumber->getCallNumber()) ?>"<?= !$smsCallNumber->isApproved() ? ' disabled="disabled"' : '' ?> /> | |
| <?php } ?> | |
| </td> | |
| <td> | |
| <label for="selectNum<?= $key ?>"> | |
| <span class="number text-darkblue bold"><?= gd_number_to_phone($smsCallNumber->getCallNumber()) ?></span> | |
| </label> | |
| </td> | |
| <td><?= gd_htmlspecialchars($smsCallNumber->getAliasName()) ?></td> | |
| <td><?= $smsCallNumber->getStatusName() ?: '-' ?></td> | |
| <td><?= $smsCallNumber->getApprovedAt() ?? '-' ?></td> | |
| <td> | |
| <?php if ($smsCallNumber->isDeletable()) { ?> | |
| <button type="button" class="btn btn-white btn-xs js-delete-call-number" data-call-number-no="<?= $smsCallNumber->getCallNumberNo() ?>" data-call-number="<?= $smsCallNumber->getCallNumber() ?>">삭제</button> | |
| <?php } elseif ($smsCallNumber->hasRejectReason()) { ?> | |
| <button type="button" class="btn btn-white btn-xs js-reject-reason" data-manage-status="<?= $smsCallNumber->getStatus() ?>" data-reject-reason="<?= gd_htmlspecialchars($smsCallNumber->getReason() ?? '') ?>">사유</button> | |
| <?php } else { ?> | |
| - | |
| <?php } ?> | |
| </td> | |
| </tr> | |
| <?php | |
| } | |
| } else { | |
| ?> | |
| <tr> | |
| <td colspan="6" class="no-data">등록된 발신번호가 없습니다.</td> | |
| </tr> | |
| <?php } ?> | |
| </tbody> | |
| </table> | |
| <div class="notice-info">발신번호 등록·삭제는 발신관리책임자(최고운영자)만 가능합니다.</div> | |
| <div class="notice-info">발신번호 회선은 최대 유선 발신번호 5회선, 무선 발신번호 3회선 까지만 등록 가능합니다.</div> | |
| <div class="notice-info">등록한 발신번호는 통합회원이 보유한 다른 쇼핑몰 어드민에도 함께 등록됩니다.</div> | |
| <div class="text-center"><?= $page->getPage('loadManageCallNumberLayer(\'PAGELINK\')') ?></div> | |
| <div class="text-center"> | |
| <button type="button" class="btn btn-white btn-lg js-register-call-number">발신번호 등록</button> | |
| <button type="button" class="btn btn-black btn-lg js-submit">발신번호 선택하기</button> | |
| </div> | |
| </div> | |
| <script type="text/javascript"> | |
| <!-- | |
| // 이 fragment 는 레이어 페이지 이동마다 재주입되므로 top-level const/let 선언 시 재선언 SyntaxError 가 발생한다 | |
| var returnInputName = <?= json_encode($returnInput) ?>; | |
| $(document).ready(function () { | |
| // 발신번호 선택하기 | |
| $('#layerManageCallNumber .js-submit').click(function (e) { | |
| var selectNum = $('#layerManageCallNumber input:radio[name=selectNum]:checked').val(); | |
| var selectNumText = $('#layerManageCallNumber input:radio[name=selectNum]:checked').attr('text'); | |
| if (typeof selectNum == 'undefined') { | |
| alert('발신번호를 선택해 주세요.'); | |
| return; | |
| } | |
| saveCallNumber(selectNum, selectNumText); | |
| }); | |
| // 발신번호 등록 | |
| $('#layerManageCallNumber .js-register-call-number').click(function (e) { | |
| var isRepresentative = <?= !empty($isRepresentative) ? 'true' : 'false' ?>; | |
| if (!isRepresentative) { | |
| dialog_alert('발신관리책임자만 이용할 수 있습니다.<br/>발신관리책임자(최고운영자)에게 문의하시기 바랍니다.'); | |
| return; | |
| } | |
| SmsCallerHubLayer.openRegistration({ | |
| onCompleted: function () { | |
| loadManageCallNumberLayer(); | |
| } | |
| }); | |
| }); | |
| // 발신번호 삭제 | |
| $('#layerManageCallNumber .js-delete-call-number').click(function (e) { | |
| var isRepresentative = <?= !empty($isRepresentative) ? 'true' : 'false' ?>; | |
| if (!isRepresentative) { | |
| dialog_alert('발신관리책임자만 이용할 수 있습니다.<br/>발신관리책임자(최고운영자)에게 문의하시기 바랍니다.'); | |
| return; | |
| } | |
| var callNumberNo = $(this).data('call-number-no'); | |
| var callNumber = String($(this).data('call-number')); | |
| // 목록의 '사용 중' 표기와 같은 기준이어야 하므로 이 몰의 현재 발신번호와 비교한다 | |
| var inUse = callNumber === <?= json_encode($currentCallNumber) ?>; | |
| var message = (inUse ? '사용 중인 발신번호입니다. 삭제하시겠습니까?' : '발신번호를 삭제하시겠습니까?') | |
| + '<br/><br/>발신번호 삭제 시, 다시 복구되지 않습니다.<br/>삭제 후 자동알림을 포함한 SMS가 발송되지 않습니다.'; | |
| dialog_confirm(message, function (result) { | |
| if (result) { | |
| var params = { | |
| mode: 'deleteCallNumber', | |
| callNumberNo: callNumberNo, | |
| callNumber: callNumber | |
| }; | |
| $.post('../member/layer_manage_call_number_ps.php', params, function (response) { | |
| if (!response || response.success !== true) { | |
| dialog_alert((response && response.message) || '일시적인 오류가 발생하였습니다.<br/>다시 시도해 주세요.'); | |
| return; | |
| } | |
| dialog_alert('발신번호 삭제가 완료되었습니다.'); | |
| loadManageCallNumberLayer(); | |
| }).fail(function (xhr) { | |
| // 권한 실패는 403 이라 success 가 아니라 여기로 들어오므로 응답 본문의 안내 문구를 그대로 노출한다 | |
| dialog_alert((xhr.responseJSON && xhr.responseJSON.message) || '일시적인 오류가 발생하였습니다.<br/>다시 시도해 주세요.'); | |
| }); | |
| } | |
| }, '발신번호 삭제'); | |
| }); | |
| // 반려·차단 사유 | |
| $('#layerManageCallNumber .js-reject-reason').click(function (e) { | |
| var reasonTitle = $(this).data('manage-status') === 'BLOCKED' ? '발신번호 차단 사유' : '발신번호 반려 사유'; | |
| dialog_alert($(this).data('reject-reason'), reasonTitle); | |
| }); | |
| }); | |
| // 폼 저장 버튼이 없는 화면(SMS 발송)에서도 열리는 레이어라 선택 시점에 바로 저장한다 | |
| function saveCallNumber(callNumber, callNumberText) { | |
| // 이 레이어는 member 외 화면(promotion 등)에서도 열리므로 상대경로를 쓰면 base 가 달라져 404 가 된다 | |
| $.post('/member/layer_manage_call_number_ps.php', {mode: 'setCallNumber', callNumber: callNumber}, function (response) { | |
| if (!response || response.success !== true) { | |
| dialog_alert((response && response.message) || '일시적인 오류가 발생하였습니다.<br/>다시 시도해 주세요.'); | |
| return; | |
| } | |
| // 부모 폼이 smsCallNum 을 그대로 재저장하는 화면(자동알림 설정)이 있어 저장 직후 입력값까지 맞춘다 | |
| $('input[name="' + returnInputName + '"]').val(callNumber); | |
| $('.smsCallNumText').html(callNumberText || callNumber); | |
| $('.smsCallNumText').addClass('number text-darkblue bold'); | |
| // dialog_alert 는 [X]·ESC 로도 닫혀 콜백이 보장되지 않으므로 레이어를 먼저 닫는다 | |
| layer_close(); | |
| dialog_alert('선택한 발신번호가 저장 완료되었습니다.', '안내'); | |
| }).fail(function (xhr) { | |
| // 권한 실패는 403 이라 success 가 아니라 여기로 들어오므로 응답 본문의 안내 문구를 그대로 노출한다 | |
| dialog_alert((xhr.responseJSON && xhr.responseJSON.message) || '일시적인 오류가 발생하였습니다.<br/>다시 시도해 주세요.'); | |
| }); | |
| } | |
| function loadManageCallNumberLayer(pagelink) { | |
| if (typeof pagelink == 'undefined') { | |
| pagelink = ''; | |
| } | |
| var parameters = { | |
| 'page': pagelink === '' ? 1 : parseInt(pagelink.replace('page=', ''), 10), | |
| 'returnInput': returnInputName | |
| }; | |
| $.post('../member/layer_manage_call_number.php', parameters, function (data) { | |
| $('#layerManageCallNumber').html(data); | |
| }).fail(function (xhr) { | |
| // 권한 실패는 403 이라 success 가 아니라 여기로 들어오므로 응답 본문의 안내 문구를 그대로 노출한다 | |
| dialog_alert((xhr.responseJSON && xhr.responseJSON.message) || '일시적인 오류가 발생하였습니다.<br/>다시 시도해 주세요.'); | |
| }); | |
| } | |
| //--> | |
| </script> | |
변경 전sms_auto.php
| <!-- //@formatter:off --> | |
| <input type="hidden" id="kakaoUse" name="kakaoUse" value="<?php echo $kakaoSetting['useFlag']; ?>"/> | |
| <input type="hidden" id="kakaoOrderUse" name="kakaoOrderUse" value="<?php echo $kakaoSetting['orderUseFlag']; ?>"/> | |
| <input type="hidden" id="kakaoMemberUse" name="kakaoMemberUse" value="<?php echo $kakaoSetting['memberUseFlag']; ?>"/> | |
| <tr> | |
| <th>SMS 발신번호</th> | |
| <td class="form-inline"> | |
| <?php if ($smsPreRegister === false) { ?> | |
| <div> | |
| <input type="hidden" name="smsCallNum" value=""/> | |
| <span class="smsCallNumText"> | |
| <span class="text-darkred">등록된 SMS 발신번호가 없습니다.</span> | |
| </span> | |
| <?php if (gd_is_provider() === false) { ?> | |
| <a href="<?= $commerceSmsIntroUrl; ?>" target="_blank" class="btn btn-gray btn-sm">발신번호 등록하기</a> | |
| <?php } ?> | |
| </div> | |
| <div class="notice-info"> | |
| 발신번호 사전등록제 : (전기통신사업법 제 84조의 2) 거짓으로 표시된 전화번호로 인한 이용자 피해 예방을 위해 사전 등록한 발신번호로만 SMS를 발송하실 수 있습니다. <a href="https://www.nhn-commerce.com/customer/board-view.gd?type=notice&idx=1247" target="_blank" class="snote bold">자세히보기 ></a> | |
| </div> | |
| <?php } elseif ($smsPreRegister === 'reset') { ?> | |
| <div> | |
| <input type="hidden" name="smsCallNum" value=""/> | |
| <span class="smsCallNumText"> | |
| <span class="text-darkred">기 설정된 SMS 발신번호는 사전 등록된 번호가 아닙니다.</span> | |
| </span> | |
| <?php if (gd_is_provider() === false) { ?> | |
| <button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 선택하기</button> | |
| <?php } ?> | |
| </div> | |
| <?php } elseif ($smsPreRegister === 'empty') { ?> | |
| <div> | |
| <input type="hidden" name="smsCallNum" value=""/> | |
| <span class="smsCallNumText"> | |
| </span> | |
| <button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 선택하기</button> | |
| </div> | |
| <?php } elseif ($smsPreRegister === true) { ?> | |
| <input type="hidden" name="smsCallNum" value="<?php echo gd_isset($smsAutoData['smsCallNum']) ?>"/> | |
| <span class="smsCallNumText number text-blue bold"><?php echo gd_number_to_phone($smsAutoData['smsCallNum']) ?></span> | |
| <?php if (gd_is_provider() === false) { ?> | |
| <button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 변경하기</button> | |
| <?php } ?> | |
| <?php } ?> | |
| <?php if ($smsPreRegister !== true) { ?> | |
| <div class="notice-danger">[SMS 설정]에 설정된 인증번호와 <a href="<?= $commerceShopMainUrl; ?>" class="btn-link text-red"><b>[마이페이지 > 쇼핑몰 관리]</b></a>에 설정된 인증번호가 일치해야 발신번호 정보가 정상 출력됩니다.</div> | |
| <?php } ?> | |
| </td> | |
| </tr> | |
| <tr> | |
| var params = { | |
| returnInput: 'smsCallNum' | |
| }; | |
| $.get('../member/layer_sms_call_number_select.php', params, function (data) { | |
| BootstrapDialog.show({ | |
| title: 'SMS 발신번호 목록', | |
| message: $(data), | |
| closable: true | |
| }); | |
| }); | |
| }); | |
변경 후sms_auto.php
| <!-- //@formatter:off --> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script> | |
| <input type="hidden" id="kakaoUse" name="kakaoUse" value="<?php echo $kakaoSetting['useFlag']; ?>"/> | |
| <input type="hidden" id="kakaoOrderUse" name="kakaoOrderUse" value="<?php echo $kakaoSetting['orderUseFlag']; ?>"/> | |
| <input type="hidden" id="kakaoMemberUse" name="kakaoMemberUse" value="<?php echo $kakaoSetting['memberUseFlag']; ?>"/> | |
| <tr> | |
| <th>SMS 발신번호</th> | |
| <td class="form-inline"> | |
| <?php if (!empty($smsAutoData['smsCallNum'])) { ?> | |
| <input type="hidden" name="smsCallNum" value="<?= gd_isset($smsAutoData['smsCallNum']) ?>"/> | |
| <span class="smsCallNumText number text-blue bold"><?= gd_number_to_phone($smsAutoData['smsCallNum']) ?></span> | |
| <?php if (gd_is_provider() === false) { ?> | |
| <button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 변경하기</button> | |
| <?php } ?> | |
| <?php } elseif (!$hasRegisteredCallNumber) { ?> | |
| <div> | |
| <input type="hidden" name="smsCallNum" value=""/> | |
| <span class="smsCallNumText"> | |
| <span class="text-darkred">등록된 SMS 발신번호가 없습니다.</span> | |
| </span> | |
| <?php if (gd_is_provider() === false) { ?> | |
| <button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 등록하기</button> | |
| <?php } ?> | |
| </div> | |
| <div class="notice-info"> | |
| 발신번호 사전등록제 : (전기통신사업법 제 84조의 2) 거짓으로 표시된 전화번호로 인한 이용자 피해 예방을 위해 사전 등록한 발신번호로만 SMS를 발송하실 수 있습니다. <a href="https://www.nhn-commerce.com/customer/board-view.gd?type=notice&idx=1247" target="_blank" class="snote bold">자세히보기 ></a> | |
| </div> | |
| <?php } else { ?> | |
| <div> | |
| <input type="hidden" name="smsCallNum" value=""/> | |
| <span class="smsCallNumText"> | |
| </span> | |
| <button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 선택하기</button> | |
| </div> | |
| <?php } ?> | |
| <div class="notice-danger">[SMS 설정]에 설정된 인증번호와 <a href="<?= $commerceShopMainUrl; ?>" class="btn-link text-red"><b>[마이페이지 > 쇼핑몰 관리]</b></a>에 설정된 인증번호가 일치해야 발신번호 정보가 정상 출력됩니다.</div> | |
| </td> | |
| </tr> | |
| <tr> | |
| var params = { | |
| returnInput: 'smsCallNum' | |
| }; | |
| $.get('../member/layer_manage_call_number.php', params, function (data) { | |
| // 200 으로 내려오는 오류는 JSON, 정상 레이어는 HTML 로 응답된다 | |
| if (typeof data === 'object' && data !== null) { | |
| dialog_alert(data.message || '일시적인 오류가 발생하였습니다.<br>다시 시도해 주세요.'); | |
| return; | |
| } | |
| BootstrapDialog.show({ | |
| title: 'SMS 발신번호 목록', | |
| message: $(data), | |
| closable: true | |
| }); | |
| }).fail(function (xhr) { | |
| var response = xhr.responseJSON; | |
| if (response && response.data && response.data.canRegisterRepresentative) { | |
| SmsCallerHubLayer.openRepresentativeRegisterGuide(); | |
| return; | |
| } | |
| if (response && response.data && response.data.isMigrationRequired && response.data.isRepresentative) { | |
| SmsCallerHubLayer.openMigration({ | |
| // 이관을 마치면 막혀 있던 발신번호 관리 레이어를 다시 연다 | |
| onCompleted: function () { | |
| $('.js-sms-call-number').click(); | |
| } | |
| }); | |
| return; | |
| } | |
| // 권한 실패는 403 이라 success 가 아니라 여기로 들어오므로 응답 본문의 안내 문구를 그대로 노출한다 | |
| dialog_alert((response && response.message) || '일시적인 오류가 발생하였습니다.<br>다시 시도해 주세요.'); | |
| }); | |
| }); | |
변경 전sms_send.php
| <script type="text/javascript"> | |
| <!-- | |
| /** | |
| // SMS 사전 등록 발신번호 선택하기 | |
| $('.js-sms-call-number').click(function (e) { | |
| var params = {returnInput: 'smsCallNum'}; | |
| $.get('../member/layer_sms_call_number_select.php', params, function (data) { | |
| BootstrapDialog.show({ | |
| title: 'SMS 발신번호 목록', | |
| message: $(data), | |
| closable: true | |
| }); | |
| }); | |
| }); | |
| <tr> | |
| <th class="require">SMS 발신번호</th> | |
| <td class="form-inline"> | |
| <?php if ($smsPreRegister === false) { ?> | |
| <div> | |
| <input type="hidden" name="smsCallNum" value=""/> | |
| <span class="smsCallNumText"> | |
| <span class="text-darkred">등록된 SMS 발신번호가 없습니다.</span> | |
| </span> | |
| <?php if (gd_is_provider() === false) { ?> | |
| <a href="<?= $commerceSmsIntroUrl; ?>" target="_blank" class="btn btn-gray btn-sm">발신번호 등록하기</a> | |
| <?php } ?> | |
| </div> | |
| <div class="notice-info"> | |
| 발신번호 사전등록제 : (전기통신사업법 제 84조의 2) 거짓으로 표시된 전화번호로 인한 이용자 피해 예방을 위해 사전 등록한 발신번호로만 SMS를 발송하실 수 있습니다. <a href="https://www.nhn-commerce.com/customer/board-view.gd?type=notice&idx=1247" target="_blank" | |
| class="snote bold btn-link">자세히보기 ></a> | |
| </div> | |
| <?php } elseif ($smsPreRegister === 'reset') { ?> | |
| <div> | |
| <input type="hidden" name="smsCallNum" value=""/> | |
| <span class="smsCallNumText"> | |
| <span class="text-darkred">기 설정된 SMS 발신번호는 사전 등록된 번호가 아닙니다.</span> | |
| </span> | |
| <?php if (gd_is_provider() === false) { ?> | |
| <button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 선택하기</button> | |
| <?php } ?> | |
| </div> | |
| <?php } elseif ($smsPreRegister === 'empty') { ?> | |
| <div> | |
| <input type="hidden" name="smsCallNum" value=""/> | |
| <span class="smsCallNumText"> | |
| </span> | |
| <button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 선택하기</button> | |
| </div> | |
| <?php } elseif ($smsPreRegister === true) { ?> | |
| <input type="hidden" name="smsCallNum" value="<?php echo gd_isset($smsCallNum) ?>"/> | |
| <span class="smsCallNumText number text-darkblue bold"><?php echo gd_number_to_phone($smsCallNum) ?></span> | |
| <?php if (gd_is_provider() === false) { ?> | |
| <button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 변경하기</button> | |
| <?php } ?> | |
| <?php } ?> | |
| <?php if ($smsPreRegister !== true) { ?> | |
| <div class="notice-danger">[SMS 설정]에 설정된 인증번호와 <a href="<?= $commerceShopMainUrl; ?>" class="btn-link text-red"><b>[마이페이지 > 쇼핑몰 관리]</b></a>에 설정된 인증번호가 일치해야 발신번호 정보가 정상 출력됩니다.</div> | |
| <?php } ?> | |
| </td> | |
| </tr> | |
변경 후sms_send.php
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script> | |
| <script type="text/javascript"> | |
| <!-- | |
| /** | |
| // SMS 사전 등록 발신번호 선택하기 | |
| $('.js-sms-call-number').click(function (e) { | |
| var params = {returnInput: 'smsCallNum'}; | |
| $.get('../member/layer_manage_call_number.php', params, function (data) { | |
| // 200 으로 내려오는 오류는 JSON, 정상 레이어는 HTML 로 응답된다 | |
| if (typeof data === 'object' && data !== null) { | |
| dialog_alert(data.message || '일시적인 오류가 발생하였습니다.<br>다시 시도해 주세요.'); | |
| return; | |
| } | |
| BootstrapDialog.show({ | |
| title: 'SMS 발신번호 목록', | |
| message: $(data), | |
| closable: true | |
| }); | |
| }).fail(function (xhr) { | |
| var response = xhr.responseJSON; | |
| if (response && response.data && response.data.canRegisterRepresentative) { | |
| SmsCallerHubLayer.openRepresentativeRegisterGuide(); | |
| return; | |
| } | |
| if (response && response.data && response.data.isMigrationRequired && response.data.isRepresentative) { | |
| SmsCallerHubLayer.openMigration({ | |
| // 이관을 마치면 막혀 있던 발신번호 관리 레이어를 다시 연다 | |
| onCompleted: function () { | |
| $('.js-sms-call-number').click(); | |
| } | |
| }); | |
| return; | |
| } | |
| // 권한 실패는 403 이라 success 가 아니라 여기로 들어오므로 응답 본문의 안내 문구를 그대로 노출한다 | |
| dialog_alert((response && response.message) || '일시적인 오류가 발생하였습니다.<br>다시 시도해 주세요.'); | |
| }); | |
| }); | |
| <tr> | |
| <th class="require">SMS 발신번호</th> | |
| <td class="form-inline"> | |
| <?php if (!empty($smsCallNum)) { ?> | |
| <input type="hidden" name="smsCallNum" value="<?= gd_isset($smsCallNum) ?>"/> | |
| <span class="smsCallNumText number text-darkblue bold"><?= gd_number_to_phone($smsCallNum) ?></span> | |
| <?php if (gd_is_provider() === false) { ?> | |
| <button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 변경하기</button> | |
| <?php } ?> | |
| <?php } elseif (!$hasRegisteredCallNumber) { ?> | |
| <div> | |
| <input type="hidden" name="smsCallNum" value=""/> | |
| <span class="smsCallNumText"> | |
| <span class="text-darkred">등록된 SMS 발신번호가 없습니다.</span> | |
| </span> | |
| <?php if (gd_is_provider() === false) { ?> | |
| <button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 등록하기</button> | |
| <?php } ?> | |
| </div> | |
| <div class="notice-info"> | |
| 발신번호 사전등록제 : (전기통신사업법 제 84조의 2) 거짓으로 표시된 전화번호로 인한 이용자 피해 예방을 위해 사전 등록한 발신번호로만 SMS를 발송하실 수 있습니다. <a href="https://www.nhn-commerce.com/customer/board-view.gd?type=notice&idx=1247" target="_blank" | |
| class="snote bold btn-link">자세히보기 ></a> | |
| </div> | |
| <?php } else { ?> | |
| <div> | |
| <input type="hidden" name="smsCallNum" value=""/> | |
| <span class="smsCallNumText"> | |
| </span> | |
| <button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 선택하기</button> | |
| </div> | |
| <?php } ?> | |
| <div class="notice-danger">[SMS 설정]에 설정된 인증번호와 <a href="<?= $commerceShopMainUrl; ?>" class="btn-link text-red"><b>[마이페이지 > 쇼핑몰 관리]</b></a>에 설정된 인증번호가 일치해야 발신번호 정보가 정상 출력됩니다.</div> | |
| </td> | |
| </tr> | |
변경 전admin_log_list.php
| <form name="frmSearch" id="frmSearch" action="admin_log_list.php" method="get" class="js-form-enter-submit"> | |
| <input type="hidden" name="view" value="<?=$view;?>"> | |
| <div class="search-detail-box"> | |
| <table class="table table-cols mgb15"> | |
| <tr> | |
| <th class="width-md">운영자 아이디</th> | |
| <td> | |
| <div class="form-inline"> | |
| <?= gd_select_box('searchKind', 'searchKind', $searchKindASelectBox, null, gd_isset($req['searchKind']), null, null, 'form-control '); ?> | |
| <input type="text" class="form-control width300" name="managerId" value="<?=$req['managerId']?>"/> | |
| </div> | |
| </td> | |
| </tr> | |
| <li role="presentation" <?=$view == 'adminAccess' ? 'class="active"' : ''?>> | |
| <a href="../policy/admin_log_list.php?view=adminAccess<?=$queryString;?>">로그인, 인증기록</a> | |
| </li> | |
| </ul> | |
| <div class="table-header"> | |
| </div> | |
| </form> | |
| <table class="table table-rows table-fixed"> | |
| <colgroup> | |
| <col class="width15p"> | |
| <col class="width15p"> | |
| <col class="width15p"> | |
| <?php if ($view == 'default') { ?><col class="width15p"><?php } ?> | |
| <?php if ($view == 'default' || $view == 'adminList') { ?><col ><?php } ?> | |
| <col class="width15p"> | |
| </colgroup> | |
| <thead> | |
| <tr> | |
| <th>접속일시</th> | |
| <th>접속IP</th> | |
| <th>운영자 아이디</th> | |
| <?php if ($view == 'default') { ?><th>메뉴구분</th><?php } ?> | |
| <?php if ($view == 'default' || $view == 'adminList') { ?><th>접속페이지<br>(개인정보관련)</th><?php } ?> | |
| <th>수행업무</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <?php foreach($data['list'] as $row){ | |
| if(!$row['page']) { | |
| $row['page'] = $row['baseUri']; | |
| } | |
| <td><?=$row['regDt']?></td> | |
| <td><?=$row['ip']?></td> | |
| <td><?=$row['managerId']?> <?=$row['deleteText']?></td> | |
| <?php if ($view == 'default') { ?><td><?=$row['menu']?></td><?php } ?> | |
| <?php if ($view == 'default' || $view == 'adminList') { ?><td><?=$row['page']?></td><?php } ?> | |
| <td> | |
| <?=$row['action']?> | |
| <?php if ($row['displayDetailLogFl'] == 'y') { ?> | |
| <button class="btn btn-white btn-sm js-detail-log" data-sno="<?=$row['sno']?>">상세</button> | |
| <?php } ?> | |
| </td> | |
| </tr> | |
| <?php }?> | |
| </tbody> | |
| </table> | |
| <?php if ($view == 'default') { ?> | |
| <div class="col-xs-12"> | |
| <div class="pull-right"> | |
| <button type="button" class="btn btn-white js-admin-log-excel">엑셀 다운로드 내역</button> | |
| </button> | |
| </div> | |
| </div> | |
| <?php } ?> | |
| <div align="center"><?=$data['page']; ?></div> | |
| <script type="text/javascript"> | |
| }); | |
| }); | |
| $(document).on('click', '.js-admin-log-excel', function (e) { | |
| var addParam = { | |
| "view": 'layer' | |
| }; | |
| layer_add_info('admin_log_excel', addParam); | |
| }); | |
| //검색어 변경 될 때 placeHolder 교체 및 검색 종류 변환 및 검색 종류 변환 | |
| var searchKeyword = $('#frmSearch input[name=managerId]'); | |
| var searchKind = $('#frmSearch #searchKind'); | |
| setKeywordPlaceholder(searchKeyword, searchKind); | |
| searchKind.change(function (e) { | |
| setKeywordPlaceholder(searchKeyword, searchKind); | |
| }); | |
| }); | |
| </script> | |
변경 후admin_log_list.php
| <form name="frmSearch" id="frmSearch" action="admin_log_list.php" method="get" class="js-form-enter-submit"> | |
| <input type="hidden" name="view" value="<?=$view;?>"> | |
| <?php // 튜닝 컨트롤러는 searchKind 를 강제하지 않아 화면 기본 검색을 전체일치로 유지하려 싣는다 — 쿼리스트링으로 덮을 수 있어 통제 수단은 아니다 ?> | |
| <input type="hidden" name="searchKind" value="equalSearch"> | |
| <div class="search-detail-box"> | |
| <table class="table table-cols mgb15"> | |
| <tr> | |
| <th class="width-md">운영자 아이디</th> | |
| <td> | |
| <div class="form-inline"> | |
| <input type="text" class="form-control width300" name="managerId" value="<?= gd_htmlspecialchars($req['managerId'] ?? '') ?>" placeholder="검색어 전체를 정확히 입력하세요."/> | |
| </div> | |
| </td> | |
| </tr> | |
| <li role="presentation" <?=$view == 'adminAccess' ? 'class="active"' : ''?>> | |
| <a href="../policy/admin_log_list.php?view=adminAccess<?=$queryString;?>">로그인, 인증기록</a> | |
| </li> | |
| <li role="presentation" <?=$view == 'smsList' ? 'class="active"' : ''?>> | |
| <a href="../policy/admin_log_list.php?view=smsList<?=$queryString;?>">SMS 관련 기록</a> | |
| </li> | |
| </ul> | |
| <div class="table-header"> | |
| </div> | |
| </form> | |
| <?php if ($view == 'smsList' && !empty($data['apiErrorFl'])): ?> | |
| <div class="notice-danger mgb0 mgl15 mgb15">SMS 관련 기록을 조회할 수 없습니다. 잠시 후 다시 시도해 주세요.</div> | |
| <?php endif; ?> | |
| <table class="table table-rows table-fixed"> | |
| <colgroup> | |
| <col class="width15p"> | |
| <col class="width15p"> | |
| <col class="width15p"> | |
| <?php if ($displayMenuColumnFl): ?><col class="width15p"><?php endif; ?> | |
| <?php if ($displayPageColumnFl): ?><col ><?php endif; ?> | |
| <col class="width15p"> | |
| </colgroup> | |
| <thead> | |
| <tr> | |
| <th><?= $view == 'smsList' ? '처리일시' : '접속일시' ?></th> | |
| <th>접속IP</th> | |
| <th>운영자 아이디</th> | |
| <?php if ($displayMenuColumnFl): ?><th>메뉴구분</th><?php endif; ?> | |
| <?php if ($displayPageColumnFl): ?><th>접속페이지<?= $displayPersonalInfoNoteFl ? '<br>(개인정보관련)' : '' ?></th><?php endif; ?> | |
| <th>수행업무</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <?php foreach ($data['list'] as $row): | |
| if(!$row['page']) { | |
| $row['page'] = $row['baseUri']; | |
| } | |
| <td><?=$row['regDt']?></td> | |
| <td><?=$row['ip']?></td> | |
| <td><?=$row['managerId']?> <?=$row['deleteText']?></td> | |
| <?php if ($displayMenuColumnFl): ?><td><?=$row['menu']?></td><?php endif; ?> | |
| <?php if ($displayPageColumnFl): ?><td><?=$row['page']?></td><?php endif; ?> | |
| <td> | |
| <?=$row['action']?> | |
| <?php if ($row['displayDetailLogFl'] == 'y'): ?> | |
| <?php if ($view == 'smsList'): ?> | |
| <button class="btn btn-white btn-sm js-sms-detail-log" data-log-id="<?=$row['logId']?>">상세</button> | |
| <?php else: ?> | |
| <button class="btn btn-white btn-sm js-detail-log" data-sno="<?=$row['sno']?>">상세</button> | |
| <?php endif; ?> | |
| <?php endif; ?> | |
| </td> | |
| </tr> | |
| <?php endforeach; ?> | |
| </tbody> | |
| </table> | |
| <?php if ($view == 'default'): ?> | |
| <div class="col-xs-12"> | |
| <div class="pull-right"> | |
| <button type="button" class="btn btn-white js-admin-log-excel">엑셀 다운로드 내역</button> | |
| </button> | |
| </div> | |
| </div> | |
| <?php endif; ?> | |
| <div align="center"><?=$data['page']; ?></div> | |
| <script type="text/javascript"> | |
| }); | |
| }); | |
| // SMS 관련 기록 상세 | |
| $('.js-sms-detail-log').on('click', function () { | |
| var addParam = { | |
| 'logId': $(this).data('log-id'), | |
| 'layerTitle': '개인정보접속기록' | |
| }; | |
| layer_add_info('sms_personal_info_access_log', addParam); | |
| }); | |
| $(document).on('click', '.js-admin-log-excel', function (e) { | |
| var addParam = { | |
| "view": 'layer' | |
| }; | |
| layer_add_info('admin_log_excel', addParam); | |
| }); | |
| }); | |
| </script> | |
변경 전manage_list.php
| <input type="text" name="keyword" value="<?= $search['keyword']; ?>" class="form-control width-xl"/> | |
| </td> | |
| </tr> | |
| <tbody class="js-search-detail"> | |
| <tr> | |
| <th>장기 미로그인</th> | |
| <th class="width10p">공급사 구분</th> | |
| <th class="width30p">아이디/닉네임</th> | |
| <th class="width7p">이름</th> | |
| <th class="width7p">직원여부</th> | |
| <th class="width10p">직원/부서/직급/직책</th> | |
| <th class="width10p">연락처</th> | |
| <td> | |
| <div><?= $val['managerNm']; ?></div> | |
| </td> | |
| <td> | |
| <?= $employeeList[$val['employeeFl']] ?> | |
| </td> | |
변경 후manage_list.php
| <input type="text" name="keyword" value="<?= $search['keyword']; ?>" class="form-control width-xl"/> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th>SMS 권한</th> | |
| <td colspan="3" class="form-inline"> | |
| <?php foreach ($smsRoleList as $smsRoleValue => $smsRoleName) { ?> | |
| <label class="radio-inline"> | |
| <input type="radio" name="smsRole" value="<?= $smsRoleValue; ?>" <?= gd_isset($checked['smsRole'][$smsRoleValue]); ?> /> <?= $smsRoleName; ?> | |
| </label> | |
| <?php } ?> | |
| </td> | |
| </tr> | |
| <tbody class="js-search-detail"> | |
| <tr> | |
| <th>장기 미로그인</th> | |
| <th class="width10p">공급사 구분</th> | |
| <th class="width30p">아이디/닉네임</th> | |
| <th class="width7p">이름</th> | |
| <th class="width7p">SMS 권한</th> | |
| <th class="width7p">직원여부</th> | |
| <th class="width10p">직원/부서/직급/직책</th> | |
| <th class="width10p">연락처</th> | |
| <td> | |
| <div><?= $val['managerNm']; ?></div> | |
| </td> | |
| <td> | |
| <div><?= gd_isset($val['smsRoleName']); ?></div> | |
| </td> | |
| <td> | |
| <?= $employeeList[$val['employeeFl']] ?> | |
| </td> | |
변경 전_manage_permission_manager_list.php
| <table class="table table-rows table-fixed mgb0"> | |
| <thead> | |
| <tr> | |
| <th class="width10p center"><input type="checkbox" id="chk_all" class="js-checkall" data-target-name="manage_sno"/></th> | |
| <th class="width10p center">번호</th> | |
| <th class="width20p center">공급사 구분</th> | |
| <th class="center">아이디/닉네임</th> | |
| <th class="width20p center">이름</th> | |
| <th class="width18p center">개별수정</th> | |
| </tr> | |
| </thead> | |
| </table> | |
| <div class="manager-list-body mgb15"> | |
| <table class="table table-rows table-fixed mgb0"> | |
| <colgroup> | |
| <col class="width10p"/> | |
| <col class="width10p"/> | |
| <col class="width20p"/> | |
| <col/> | |
| <col class="width20p"/> | |
| <col class="width18p"/> | |
| </colgroup> | |
| <tbody> | |
| <?php | |
| $superText = ($val['sno'] == 1) ? '<br/><span class="text-blue">(최고운영자)</span>' : '<br/><span class="text-blue">(대표운영자)</span>'; | |
| } | |
| ?> | |
| <tr data-sno="<?= $val['sno']; ?>" data-issuper="<?= $val['isSuper']; ?>"> | |
| <td class="center"> | |
| <input type="checkbox" name="manage_sno[]" value="<?= $val['sno']; ?>" /> | |
| </td> | |
| <?= $superText ?> | |
| </td> | |
| <td class="center"><div><?= $val['managerNm']; ?></div></td> | |
| <td class="center"><input type="button" value="권한보기" class="btn btn-white btn-view-unit-permission"></td> | |
| </tr> | |
| <?php | |
| } else { | |
| ?> | |
| <tr> | |
| <td class="center" colspan="6">검색된 정보가 없습니다.</td> | |
| </tr> | |
| <?php | |
| } | |
변경 후_manage_permission_manager_list.php
| <table class="table table-rows table-fixed mgb0"> | |
| <thead> | |
| <tr> | |
| <th class="width5p center"><input type="checkbox" id="chk_all" class="js-checkall" data-target-name="manage_sno"/></th> | |
| <th class="width7p center">번호</th> | |
| <th class="width20p center">공급사 구분</th> | |
| <th class="center">아이디/닉네임</th> | |
| <th class="width20p center">이름</th> | |
| <th class="width15p center">SMS 권한</th> | |
| <th class="width11p center">개별수정</th> | |
| </tr> | |
| </thead> | |
| </table> | |
| <div class="manager-list-body mgb15"> | |
| <table class="table table-rows table-fixed mgb0"> | |
| <colgroup> | |
| <col class="width5p"/> | |
| <col class="width7p"/> | |
| <col class="width20p"/> | |
| <col/> | |
| <col class="width20p"/> | |
| <col class="width15p"/> | |
| <col class="width11p"/> | |
| </colgroup> | |
| <tbody> | |
| <?php | |
| $superText = ($val['sno'] == 1) ? '<br/><span class="text-blue">(최고운영자)</span>' : '<br/><span class="text-blue">(대표운영자)</span>'; | |
| } | |
| ?> | |
| <tr data-sno="<?= $val['sno']; ?>" data-issuper="<?= $val['isSuper']; ?>" data-sms-role="<?= gd_isset($val['smsRole']); ?>" data-sms-permission-locked="<?= $val['smsPermissionLocked']; ?>"> | |
| <td class="center"> | |
| <input type="checkbox" name="manage_sno[]" value="<?= $val['sno']; ?>" /> | |
| </td> | |
| <?= $superText ?> | |
| </td> | |
| <td class="center"><div><?= $val['managerNm']; ?></div></td> | |
| <td class="center"><div><?= gd_isset($val['smsRoleName']); ?></div></td> | |
| <td class="center"><input type="button" value="권한보기" class="btn btn-white btn-view-unit-permission"></td> | |
| </tr> | |
| <?php | |
| } else { | |
| ?> | |
| <tr> | |
| <td class="center" colspan="7">검색된 정보가 없습니다.</td> | |
| </tr> | |
| <?php | |
| } | |
변경 전_manage_permission_menu_list.php
| } | |
| break; | |
| } | |
| default: | |
| if ($adminMenuType == 'd' && $isSuper == 'y') { // 본사 최고운영자 수정 일 경우 | |
| $disabled['functionAuth'][$code] = 'disabled="disabled"'; | |
| break; | |
| } | |
| ?> | |
| <li class="dropdown-item"><label><input type="checkbox" name="functionAuth[<?= $code; ?>]" data-value="<?= $code; ?>" value="y" <?= gd_isset($disabled['functionAuth'][$code]); ?> <?= gd_isset($checked['functionAuth'][$code]['y']); ?> /> <?= $name; ?></label></li> | |
| <?php | |
| // 속도 높이기 위해 불필요해진 변수 unset | |
| unset($disabled['functionAuth'][$code]); | |
| ?> | |
| </table> | |
| </div> | |
| <div class="access-control-reason <?=(!empty($sno) || $changeType == 'init') ? '' : 'display-none'?>" style="clear: both;"> | |
| <div class="table-action-dropdown"> | |
| <div class="table-action mgt0 mgb15"> | |
| $('input:checkbox[name="manage_sno[]"]:checked').length > 0 ? $('.manage-btn').prop('disabled', false) : $('.manage-btn').prop('disabled', true); | |
| } | |
| }); | |
| } | |
| }); | |
변경 후_manage_permission_menu_list.php
| } | |
| break; | |
| } | |
| case 'messageSendSms': | |
| if ($adminMenuType == 'd') { | |
| if (!empty($isMessageApprover)) { | |
| $disabled['functionAuth'][$code] = 'data-disabled-lock="lock" disabled="disabled"'; | |
| $checked['functionAuth'][$code]['y'] = 'checked="checked"'; | |
| } else { | |
| $disabled['functionAuth'][$code] = 'data-disabled-lock="lock"'; | |
| } | |
| break; | |
| } | |
| // 그 외에는 일반 기능권한과 동일 동작 (default 로 진행) | |
| default: | |
| if ($adminMenuType == 'd' && $isSuper == 'y') { // 본사 최고운영자 수정 일 경우 | |
| $disabled['functionAuth'][$code] = 'disabled="disabled"'; | |
| break; | |
| } | |
| ?> | |
| <li class="dropdown-item"><label><input type="checkbox" name="functionAuth[<?= $code; ?>]" data-value="<?= $code; ?>" value="y" data-origin-checked="<?= !empty($checked['functionAuth'][$code]['y']) ? 'y' : 'n'; ?>" <?= gd_isset($disabled['functionAuth'][$code]); ?> <?= gd_isset($checked['functionAuth'][$code]['y']); ?> /> <?= $name; ?></label></li> | |
| <?php | |
| // 속도 높이기 위해 불필요해진 변수 unset | |
| unset($disabled['functionAuth'][$code]); | |
| ?> | |
| </table> | |
| </div> | |
| <input type="hidden" id="messageSenderMeta" data-editor-approver="<?= !empty($isEditorMessageApprover) ? 'y' : 'n'; ?>" data-editor-representative="<?= !empty($isEditorMessageRepresentative) ? 'y' : 'n'; ?>" data-caller-blocked="<?= !empty($isCallerBlocked) ? 'y' : 'n'; ?>" data-sender-count="<?= (int)(gd_isset($messageSenderCount, 0)); ?>" data-sender-limit="<?= (int)(gd_isset($messageSenderLimit, 0)); ?>"/> | |
| <div class="access-control-reason <?=(!empty($sno) || $changeType == 'init') ? '' : 'display-none'?>" style="clear: both;"> | |
| <div class="table-action-dropdown"> | |
| <div class="table-action mgt0 mgb15"> | |
| $('input:checkbox[name="manage_sno[]"]:checked').length > 0 ? $('.manage-btn').prop('disabled', false) : $('.manage-btn').prop('disabled', true); | |
| } | |
| }); | |
| } else if ($('#menuList input:checkbox[data-value="messageSendSms"]').length > 0) { | |
| $('.access-control-reason').removeClass('display-none'); | |
| } | |
| }); | |
변경 전manage_permission.php
| </tr> | |
| <?php } ?> | |
| <?php } ?> | |
| <tr> | |
| <th>검색어</th> | |
| <td class="form-inline"> | |
| } | |
| } | |
| form.target = 'ifrmProcess'; | |
| form.submit(); | |
| }, | |
변경 후manage_permission.php
| </tr> | |
| <?php } ?> | |
| <?php } ?> | |
| <tr> | |
| <th>SMS 권한</th> | |
| <td colspan="3" class="form-inline"> | |
| <?php foreach ($smsRoleList as $smsRoleValue => $smsRoleName) { ?> | |
| <label class="radio-inline"> | |
| <input type="radio" name="smsRole" value="<?= $smsRoleValue; ?>" <?= $smsRoleValue === 'all' ? 'checked="checked"' : ''; ?> /> <?= $smsRoleName; ?> | |
| </label> | |
| <?php } ?> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th>검색어</th> | |
| <td class="form-inline"> | |
| } | |
| } | |
| // 최고운영자와 부운영자 동시 선택 차단 | |
| var $smsCheckedRows = $('input:checkbox[name="manage_sno[]"]:checked').closest('tr'); | |
| if ($smsCheckedRows.length > 1 && $smsCheckedRows.filter('[data-issuper="y"]').length > 0) { | |
| dialog_alert('최고운영자와 부운영자를 동시에 수정할 수 없습니다.', '정보'); | |
| return false; | |
| } | |
| // SMS 발송담당자(messageSendSms) 권한 변경 검증 — 다건 대상 합산 | |
| // 전체권한(permissionFl='s') 모드에선 disabled 라 저장 직렬화(:checked:not(:disabled))·서버 모두 미반영 → 검증 스킵 | |
| var $smsSetBox = $('#menuList input:checkbox[data-value="messageSendSms"]:not(:disabled)'); | |
| if ($smsSetBox.length > 0) { | |
| var smsSetGranted = $smsSetBox.is(':checked'); | |
| var smsChanged = false, smsNewSenderCount = 0; | |
| $smsCheckedRows.each(function () { | |
| var smsRole = $(this).data('smsRole'); | |
| // 잠금 판정은 서버가 행마다 내려준다 — 미등록 최고운영자는 smsRole 이 비어 있어 역할값만으로는 걸러지지 않는다 | |
| if ($(this).data('smsPermissionLocked') === 'y') { | |
| return; // 최고운영자/위임자는 messageSendSms 강제라 대상 아님 | |
| } | |
| var nowSender = (smsRole === 'sender'); | |
| if (nowSender !== smsSetGranted) { | |
| smsChanged = true; | |
| } | |
| if (smsSetGranted && !nowSender) { | |
| smsNewSenderCount++; | |
| } | |
| }); | |
| if (smsChanged) { | |
| var $smsMeta = $('#messageSenderMeta'); | |
| if ($smsMeta.data('editorApprover') !== 'y') { | |
| dialog_alert('SMS 발송 권한은 발신관리책임자 또는 발신관리위임자만 부여할 수 있습니다.<br>해당 권한을 해제한 후 다시 저장해 주세요.', '경고'); | |
| return false; | |
| } | |
| if ($smsMeta.data('editorRepresentative') === 'y' && $smsMeta.data('callerBlocked') === 'y') { | |
| dialog_alert('SMS 발송 메뉴 접근이 제한되었습니다.<br>스팸 또는 부정 사용으로 차단되어 접근할 수 없습니다.<br>자세한 사항은 1:1문의로 문의하시기 바랍니다.', '경고'); | |
| return false; | |
| } | |
| if (smsSetGranted) { | |
| var smsSenderCount = parseInt($smsMeta.data('senderCount'), 10) || 0; | |
| var smsSenderLimit = parseInt($smsMeta.data('senderLimit'), 10) || 0; | |
| if (smsSenderCount + smsNewSenderCount > smsSenderLimit) { | |
| dialog_alert('발송담당자는 최대 ' + smsSenderLimit + '명까지 지정 가능합니다.', '경고'); | |
| return false; | |
| } | |
| } | |
| if ($.trim($('#accessControlReason').val()) === '') { | |
| dialog_alert('발송담당자 권한 변경 사유를 작성해주세요.', '경고'); | |
| return false; | |
| } | |
| } | |
| } | |
| form.target = 'ifrmProcess'; | |
| form.submit(); | |
| }, | |
변경 전manage_register.php
| 기본정보 | |
| </div> | |
| <table class="table table-cols"> | |
| <colgroup> | |
| <col class="width-md"/> | |
| <tr> | |
| <th class="require">이름</th> | |
| <td class="form-inline"> | |
| <input type="text" name="managerNm" value="<?= gd_isset($data['managerNm']); ?>" class="form-control js-maxlength" maxlength="20"/> | |
| </td> | |
| <th>닉네임</th> | |
| <td class="form-inline"> | |
| <tr> | |
| <th>휴대폰번호</th> | |
| <td class="form-inline" colspan="3"> | |
| <label title="휴대폰 번호를 입력해 주세요!"> | |
| <input type="text" name="cellPhone" value="<?= $data['cellPhone']; ?>" maxlength="12" class="form-control js-number-only width-md"/> | |
| </label> | |
| <div class="notice-info"> | |
| SMS발신번호 및 SMS인증번호 관리는 <a href="/member/sms_auto.php" target="_blank" class="btn-link">[회원 > SMS관리 > SMS 설정]</a> 에서 확인할 수 있습니다. | |
| </div> | |
| </td> | |
| </tr> | |
| <th>이메일</th> | |
| <td class="form-inline" colspan="3"> | |
| <label title="이메일을 입력해 주세요!"> | |
| <input type="text" name="email[]" value="<?= gd_isset($data['email'][0]); ?>" class="form-control width-md"/> | |
| @ | |
| <input type="text" id="email" name="email[]" value="<?= gd_isset($data['email'][1]); ?>" class="form-control width-md"/> | |
| <?= gd_select_box('email_domain', null, $emailDomain, null, $data['email'][1]); ?> | |
| </label> | |
| <button type="button" class="btn btn-gray btn-sm js-request-authemail">인증번호받기</button> | |
| <span id="emailAuthSendBox" class="<?php if (gd_isset($data['isEmailAuth']) === 'y') { ?>display-none<?php } ?>"> | |
| 인증번호 : | |
| <a href="/policy/manage_security.php" target="_blank" class="btn-link">운영보안설정 바로가기 ></a> | |
| <?php } ?> | |
| </div> | |
| </td> | |
| </tr> | |
| <col/> | |
| </colgroup> | |
| <tbody> | |
| <tr> | |
| <th>운영자 IP 접속제한</th> | |
| <td> | |
| <script type="text/javascript"> | |
| <!-- | |
| var isUseProvider = '<?=gd_use_provider()?>'; | |
| var manage_register = { | |
| regex: { | |
| auth_no: /\d{8}/ | |
| $(document).ready(function () { | |
| $('.sms-point').text(manage_register.sms_point); | |
| $.validator.addMethod('smsAuto', function(value){ | |
| if (value == 'n') { | |
| return true; | |
| onkeyup: false, | |
| ignore: ':hidden', | |
| submitHandler: function (form) { | |
| if ($('input[name="permissionSetupMode"]').length == 1) { // 운영자 권한 설정 방식 경우 | |
| if ($('input[name="permissionFl"]').length == 0) { | |
| alert("메뉴 권한은 최소 1개 이상 '읽기' 또는 '읽기+쓰기'로 설정하셔야 합니다."); | |
| }); | |
| } | |
| form.target = 'ifrmProcess'; | |
| form.submit(); | |
| }, | |
| }); | |
| $('.js-request-authno').bind('click', function () { | |
| if (manage_register.sms_point < 1) { | |
| alert('SMS 포인트가 부족합니다. 인증번호 발송을 위해 SMS 포인트를 충전해주세요'); | |
| return false; | |
| } | |
| var cellPhoneFlag = true; | |
| var $sms_auth_send_box = $('#smsAuthSendBox'); | |
| }); | |
| }); | |
| $('.js-request-authemail').bind('click', function () { | |
| var emailFlag = true; | |
| var $email_auth_send_box = $('#emailAuthSendBox'); | |
| $('input[name*=\'cellPhone\']').number_only(); | |
| if ($('input[name="permissionSetupMode"]').length == 1) { // 운영자 권한 설정 방식 경우 | |
| $('input[name=scmFl][value=n]').bind('click', function () { | |
| if ($('input[name="scmNo"]').length > 0) { | |
| $('.btn-icon-delete').trigger('click'); | |
| } | |
| } | |
| function layer_register(typeStr, mode) { | |
| var addParam = { | |
| "mode": mode, | |
| if (typeStr == 'scm') { | |
| addParam['callFunc'] = 'setScmSelect'; | |
| $('input:radio[name=scmFl]:input[value=y]').prop("checked", true); | |
| $('select[name="add_must_info_sel"]').html("<option>= 상품 필수 정보 선택 =</option>"); | |
| $('select[name="add_goods_info_sel"]').html("<option>= 추가 상품 그룹 정보 선택 =</option>"); | |
| } | |
| } | |
| function setScmSelect(data) { | |
| //공급사 값 세팅 | |
| displayTemplate(data); | |
| if ($('input[name="permissionSetupMode"]').length != 1) { // 운영자 권한 설정 방식이 아닌 경우(기존) | |
| developmentPermissionToggle(data); | |
| changePermissionLayout(data); | |
변경 후manage_register.php
| 기본정보 | |
| </div> | |
| <?php if (!empty($superManagerCommerceGuide)): ?> | |
| <div class="mgb10"> | |
| <div class="notice-info"><strong>최고운영자 정보(이름/휴대폰번호/이메일)는 통합회원 마이페이지에서 수정할 수 있습니다.</strong></div> | |
| <div class="notice-info">보안 강화를 위해 계정당 1인 1계정 사용 환경으로 개선되어, 최고운영자 정보는 통합회원 정보와 항상 동일하게 유지됩니다.</div> | |
| <div class="notice-info">통합회원 마이페이지에서 이름/휴대폰번호/이메일을 수정하시면, 최고운영자 정보에도 동일하게 반영됩니다. <a href="https://accounts.godo.co.kr/mypage/acc" target="_blank" class="btn-link">통합회원 정보 수정하러 가기 ></a></div> | |
| <div class="notice-info">통합회원이 아닌 담당자가 최고운영자 계정을 이용하고 계셨다면, 부운영자 계정을 별도로 등록해 이용해 주세요.</div> | |
| </div> | |
| <?php endif; ?> | |
| <table class="table table-cols"> | |
| <colgroup> | |
| <col class="width-md"/> | |
| <tr> | |
| <th class="require">이름</th> | |
| <td class="form-inline"> | |
| <input type="text" name="managerNm" value="<?= gd_isset($data['managerNm']); ?>" class="form-control js-maxlength" maxlength="20"<?= !empty($senderNameLocked) ? ' disabled="disabled"' : ''; ?>/> | |
| <?php if (!empty($superManagerCommerceLinked)): ?> | |
| <div class="notice-info"><a href="https://accounts.godo.co.kr/mypage/acc" target="_blank" class="btn-link">통합 마이페이지</a>에서 수정할 수 있습니다.</div> | |
| <?php endif; ?> | |
| </td> | |
| <th>닉네임</th> | |
| <td class="form-inline"> | |
| <tr> | |
| <th>휴대폰번호</th> | |
| <td class="form-inline" colspan="3"> | |
| <?php if (!empty($superManagerCommerceLinked)): ?> | |
| <label title="휴대폰 번호를 입력해 주세요!"> | |
| <input type="text" name="cellPhone" value="<?= $data['cellPhone']; ?>" maxlength="12" class="form-control js-number-only width-md" disabled="disabled"/> | |
| </label> | |
| <?php if ($selfCertification['enabled']): ?> | |
| <button type="button" class="btn btn-gray btn-sm js-request-self-certification">본인인증</button> | |
| <span class="my-sms-auth js-self-certification-state<?= $selfCertification['certified'] ? '' : ' text-red'; ?>"> | |
| <?= $selfCertification['certified'] ? '(인증완료)' : '(미인증)'; ?> | |
| </span> | |
| <?php endif; ?> | |
| <div class="notice-info"><a href="https://accounts.godo.co.kr/mypage/acc" target="_blank" class="btn-link">통합 마이페이지</a>에서 수정할 수 있습니다.</div> | |
| <?php if ($selfCertification['enabled']): ?> | |
| <div class="notice-info"> | |
| 인증이 완료된 휴대폰번호는 관리자페이지 보안로그인 및 화면보안접속 시 인증정보로 사용할 수 있습니다. | |
| <?php if (!$isProvider): ?> | |
| <a href="/policy/manage_security.php" target="_blank" class="btn-link">운영보안설정 바로가기 ></a> | |
| <?php endif; ?> | |
| </div> | |
| <?php endif; ?> | |
| <?php elseif ($selfCertification['enabled']): ?> | |
| <label title="휴대폰 번호를 입력해 주세요!"> | |
| <input type="text" name="cellPhone" value="<?= $data['cellPhone']; ?>" maxlength="12" class="form-control js-number-only width-md"<?= !empty($senderCellPhoneLocked) ? ' disabled="disabled"' : ''; ?>/> | |
| </label> | |
| <button type="button" class="btn btn-gray btn-sm js-request-self-certification">본인인증</button> | |
| <span class="my-sms-auth js-self-certification-state<?= $selfCertification['certified'] ? '' : ' text-red'; ?>"> | |
| <?= $selfCertification['certified'] ? '(인증완료)' : '(미인증)'; ?> | |
| </span> | |
| <div class="notice-info"> | |
| 인증이 완료된 휴대폰번호는 관리자페이지 보안로그인 및 화면보안접속 시 인증정보로 사용할 수 있습니다. | |
| <?php if (!$isProvider): ?> | |
| <a href="/policy/manage_security.php" target="_blank" class="btn-link">운영보안설정 바로가기 ></a> | |
| <?php endif; ?> | |
| </div> | |
| <?php else: ?> | |
| <label title="휴대폰 번호를 입력해 주세요!"> | |
| <input type="text" name="cellPhone" value="<?= $data['cellPhone']; ?>" maxlength="12" class="form-control js-number-only width-md"/> | |
| </label> | |
| <div class="notice-info"> | |
| SMS발신번호 및 SMS인증번호 관리는 <a href="/member/sms_auto.php" target="_blank" class="btn-link">[회원 > SMS관리 > SMS 설정]</a> 에서 확인할 수 있습니다. | |
| </div> | |
| <?php endif; ?> | |
| </td> | |
| </tr> | |
| <th>이메일</th> | |
| <td class="form-inline" colspan="3"> | |
| <label title="이메일을 입력해 주세요!"> | |
| <input type="text" name="email[]" value="<?= gd_isset($data['email'][0]); ?>" class="form-control width-md"<?= !empty($superManagerCommerceLinked) ? ' disabled="disabled"' : ''; ?>/> | |
| @ | |
| <input type="text" id="email" name="email[]" value="<?= gd_isset($data['email'][1]); ?>" class="form-control width-md"<?= !empty($superManagerCommerceLinked) ? ' disabled="disabled"' : ''; ?>/> | |
| <?= gd_select_box('email_domain', null, $emailDomain, null, $data['email'][1], null, !empty($superManagerCommerceLinked) ? 'disabled="disabled"' : null); ?> | |
| </label> | |
| <?php if (!empty($superManagerCommerceLinked)): ?> | |
| <div class="notice-info"><a href="https://accounts.godo.co.kr/mypage/acc" target="_blank" class="btn-link">통합 마이페이지</a>에서 수정할 수 있습니다.</div> | |
| <?php else: ?> | |
| <button type="button" class="btn btn-gray btn-sm js-request-authemail">인증번호받기</button> | |
| <span id="emailAuthSendBox" class="<?php if (gd_isset($data['isEmailAuth']) === 'y') { ?>display-none<?php } ?>"> | |
| 인증번호 : | |
| <a href="/policy/manage_security.php" target="_blank" class="btn-link">운영보안설정 바로가기 ></a> | |
| <?php } ?> | |
| </div> | |
| <?php endif; ?> | |
| </td> | |
| </tr> | |
| <col/> | |
| </colgroup> | |
| <tbody> | |
| <?php if (!empty($messageDelegate['visible'])): // SMS 발신 위임자 권한 (권한 설정 테이블 row, 지정/회수는 최고운영자만) ?> | |
| <tr class="js-message-delegate-area"> | |
| <th>SMS 발신 위임자 권한 설정</th> | |
| <td class="form-inline"> | |
| <input type="hidden" name="messageDelegateAction" id="messageDelegateAction" value=""> | |
| <?php if ($messageDelegate['assigned'] === true): ?> | |
| <span class="js-message-delegate-done">권한 설정 완료</span> | |
| <?php if ($messageDelegate['editable'] === true): ?> | |
| <button type="button" class="btn btn-sm btn-gray js-message-delegate-release">위임자 권한 해제</button> | |
| <?php endif; ?> | |
| <?php elseif ($messageDelegate['editable'] === true): ?> | |
| <button type="button" class="btn btn-sm btn-gray js-message-delegate-assign">위임자 권한 설정</button> | |
| <?php else: ?> | |
| <span class="notice-info">미지정</span> | |
| <?php endif; ?> | |
| <br> | |
| <span class="notice-info">문자를 발송할 '발송담당자' 지정은 위임과 다르므로 <a href="/policy/manage_permission.php" target="_blank" class="btn-link">[설정 > 관리 정책 > 운영자 관리]</a> 페이지의 회원 추가설정 중 'SMS 발송' 권한을 설정해 주세요.</span> | |
| </td> | |
| </tr> | |
| <?php if ($messageDelegate['assigned'] === false && $messageDelegate['editable'] === true): ?> | |
| <tr class="js-message-delegate-set-reason-row" style="display:none;"> | |
| <th>SMS 발신 위임자 권한 설정 사유</th> | |
| <td> | |
| <input type="text" id="messageDelegateSetReason" class="form-control width-2xl" maxlength="50"> | |
| <span class="notice-info">SMS 발송 위임 권한을 설정하기 위한 명확한 사유를 입력해 주세요.</span> | |
| </td> | |
| </tr> | |
| <?php endif; ?> | |
| <?php if ($messageDelegate['assigned'] === true && $messageDelegate['editable'] === true): ?> | |
| <tr class="js-message-delegate-release-reason-row" style="display:none;"> | |
| <th>SMS 발신 위임자 권한 해제 사유</th> | |
| <td> | |
| <input type="text" id="messageDelegateReleaseReason" class="form-control width-2xl" maxlength="50"> | |
| <span class="notice-info">SMS 발송 위임 권한을 해제하기 위한 명확한 사유를 입력해 주세요.</span> | |
| </td> | |
| </tr> | |
| <?php endif; ?> | |
| <?php endif; ?> | |
| <tr> | |
| <th>운영자 IP 접속제한</th> | |
| <td> | |
| <script type="text/javascript"> | |
| <!-- | |
| var isUseProvider = '<?=gd_use_provider()?>'; | |
| // 저장 게이트는 서버(ManagerEditPolicy::canEditAccount)와 같은 판정이라 휴대폰 인증 방식 분기와 분리한다 | |
| // 통합계정 연동된 최고운영자 화면은 3개 필드만 잠그고 나머지 항목 저장은 열어 둔다 (잠긴 필드는 저장 시 서버가 기존 값을 유지) | |
| var isManagerInfoModifiable = <?= !empty($managerInfoModifiable) ? 'true' : 'false' ?>; | |
| var isSelfCertificationTarget = <?= !empty($selfCertification['isSelf']) ? 'true' : 'false' ?>; | |
| const delegateActionAssign = 'assign'; | |
| const delegateActionRelease = 'release'; | |
| const isDelegateCallerBlocked = <?= !empty($messageDelegate['callerBlocked']) ? 'true' : 'false' ?>; | |
| const isDelegateBlockedByExisting = <?= !empty($messageDelegate['blockedByExisting']) ? 'true' : 'false' ?>; | |
| const delegateCallerBlockedMessage = 'SMS 발송 메뉴 접근이 제한되었습니다.<br>스팸 또는 부정 사용으로 차단되어 접근할 수 없습니다.<br>자세한 사항은 1:1문의로 문의하시기 바랍니다.'; | |
| const delegateBlockedByExistingMessage = '상점에는 최대 1명의 위임자만 설정할 수 있습니다.<br>이미 권한을 갖고 있는 운영자가 있어 권한을 설정할 수 없습니다.'; | |
| var manage_register = { | |
| regex: { | |
| auth_no: /\d{8}/ | |
| $(document).ready(function () { | |
| $('.sms-point').text(manage_register.sms_point); | |
| // SMS 발신 위임자 권한 — 버튼 클릭 시 사유 입력행 노출 + 처리 의도(action) 세팅 | |
| $('.js-message-delegate-assign').click(function () { | |
| if (isDelegateCallerBlocked) { | |
| dialog_alert(delegateCallerBlockedMessage, '경고'); | |
| return; | |
| } | |
| if (isDelegateBlockedByExisting) { | |
| dialog_alert(delegateBlockedByExistingMessage, '안내'); | |
| return; | |
| } | |
| $('#messageDelegateAction').val(delegateActionAssign); | |
| $('.js-message-delegate-set-reason-row').show(); | |
| $('#messageDelegateSetReason').focus(); | |
| }); | |
| $('.js-message-delegate-release').click(function () { | |
| $('#messageDelegateAction').val(delegateActionRelease); | |
| $('.js-message-delegate-release-reason-row').show(); | |
| $('#messageDelegateReleaseReason').focus(); | |
| }); | |
| $.validator.addMethod('smsAuto', function(value){ | |
| if (value == 'n') { | |
| return true; | |
| onkeyup: false, | |
| ignore: ':hidden', | |
| submitHandler: function (form) { | |
| if (!isManagerInfoModifiable) { | |
| // 1차(UI) 차단 — 서버 예외로 흐르면 1회성 CSRF 토큰만 소비되어 재시도 시 "잘못된 경로" 가 된다 | |
| alert('해당 계정 정보는 책임자 및 본인만 수정할 수 있습니다.'); | |
| return false; | |
| } | |
| if ($('input[name="permissionSetupMode"]').length == 1) { // 운영자 권한 설정 방식 경우 | |
| if ($('input[name="permissionFl"]').length == 0) { | |
| alert("메뉴 권한은 최소 1개 이상 '읽기' 또는 '읽기+쓰기'로 설정하셔야 합니다."); | |
| }); | |
| } | |
| // SMS 발신 위임자 권한 — 설정/해제 사유 필수 검증 + 전송값 세팅 | |
| var $mdAction = $('#messageDelegateAction'); | |
| if ($mdAction.length && $mdAction.val() !== '') { | |
| if (isDelegateCallerBlocked) { | |
| dialog_alert(delegateCallerBlockedMessage, '경고'); | |
| return false; | |
| } | |
| var mdReason = $mdAction.val() === delegateActionRelease | |
| ? $.trim($('#messageDelegateReleaseReason').val()) | |
| : $.trim($('#messageDelegateSetReason').val()); | |
| if (mdReason === '') { | |
| dialog_alert($mdAction.val() === delegateActionRelease | |
| ? '발신관리위임자 권한 해제 사유를 작성해주세요.' | |
| : '발신관리위임자 권한 설정 사유를 작성해주세요.', '경고'); | |
| return false; | |
| } | |
| // 위임자 사유를 권한 변경 사유(accessControlReason)로 제출 → 운영자 관리/수정 개인정보접속기록에 기록 | |
| var $mdReasonField = $('#frmManager input[name="accessControlReason"]'); | |
| if ($mdReasonField.length === 0) { | |
| $mdReasonField = $('<input>', {type: 'hidden', name: 'accessControlReason'}).appendTo('#frmManager'); | |
| } | |
| $mdReasonField.val(mdReason); | |
| } | |
| form.target = 'ifrmProcess'; | |
| form.submit(); | |
| }, | |
| }); | |
| $('.js-request-authno').bind('click', function () { | |
| var cellPhoneFlag = true; | |
| var $sms_auth_send_box = $('#smsAuthSendBox'); | |
| }); | |
| }); | |
| <?php if ($selfCertification['enabled']): ?> | |
| $('.js-request-self-certification').on('click', function () { | |
| // 본인인증은 등록된 계정 정보의 본인확인 수단이라 본인 계정에서만 진행한다 | |
| if (!isSelfCertificationTarget) { | |
| alert('본인 계정의 정보만 수정할 수 있습니다.'); | |
| return; | |
| } | |
| if ($.trim($('input[name="cellPhone"]').val()) === '') { | |
| alert('책임자에게 휴대폰 번호 등록을 먼저 요청해 주세요.'); | |
| return; | |
| } | |
| managerCertification.certify({ | |
| onComplete: function (success, reason) { | |
| if (success) { | |
| $('.js-self-certification-state').removeClass('text-red').text('(인증완료)'); | |
| alert('본인인증이 완료되었습니다.'); | |
| return; | |
| } | |
| if (reason === 'mismatch') { | |
| dialog_alert('입력하신 정보가 등록된 계정 정보와 일치하지 않습니다.<br>다시 확인해 주세요.', '경고'); | |
| return; | |
| } | |
| if (reason === 'duplicateDi') { | |
| dialog_alert('이미 등록된 SMS 관리자입니다.<br>SMS 관리자 권한은 한 사람이 중복으로 보유할 수 없습니다.', '경고'); | |
| return; | |
| } | |
| alert('본인인증에 실패했습니다. 인증 정보를 확인 후 다시 시도해 주세요.'); | |
| } | |
| }); | |
| }); | |
| <?php endif; ?> | |
| $('.js-request-authemail').bind('click', function () { | |
| var emailFlag = true; | |
| var $email_auth_send_box = $('#emailAuthSendBox'); | |
| $('input[name*=\'cellPhone\']').number_only(); | |
| if ($('input[name="permissionSetupMode"]').length == 1) { // 운영자 권한 설정 방식 경우 | |
| $('input[name="scmFl"]').on('click', function () { | |
| if ($(this).val() === 'n') { | |
| scmFlConfirmed = 'n'; // 본사 직접 선택은 즉시 확정 | |
| } | |
| toggleMessageDelegateAreaByScm(); | |
| }); | |
| toggleMessageDelegateAreaByScm(); // 초기 노출 상태 | |
| $('input[name=scmFl][value=n]').bind('click', function () { | |
| if ($('input[name="scmNo"]').length > 0) { | |
| $('.btn-icon-delete').trigger('click'); | |
| } | |
| } | |
| var scmFlConfirmed = $('input[name="scmFl"]:checked').val() || 'n'; // 확정된 공급사 구분 — 모달에서 공급사를 실제 선택(확인)해야만 'y' | |
| // 공급사 구분에 따라 발신관리위임자 권한 설정 영역 노출/숨김 — 공급사 운영자에겐 미노출(서버 assertAssignableTarget 와 이중 방어) | |
| function toggleMessageDelegateAreaByScm() { | |
| var isHeadOffice = $('input[name="scmFl"]:checked').val() !== 'y'; | |
| $('.js-message-delegate-area').toggle(isHeadOffice); | |
| if (isHeadOffice === false) { | |
| $('#messageDelegateAction').val(''); | |
| $('.js-message-delegate-set-reason-row, .js-message-delegate-release-reason-row').hide(); | |
| } | |
| } | |
| function layer_register(typeStr, mode) { | |
| var addParam = { | |
| "mode": mode, | |
| if (typeStr == 'scm') { | |
| addParam['callFunc'] = 'setScmSelect'; | |
| // 라디오는 모달에서 공급사를 실제 선택(확인)해야 공급사로 변경 — 열기 시점엔 확정값 유지(미선택 닫기 시 자연히 본사 유지) | |
| $('input[name="scmFl"][value="' + scmFlConfirmed + '"]').prop('checked', true); | |
| toggleMessageDelegateAreaByScm(); | |
| $('select[name="add_must_info_sel"]').html("<option>= 상품 필수 정보 선택 =</option>"); | |
| $('select[name="add_goods_info_sel"]').html("<option>= 추가 상품 그룹 정보 선택 =</option>"); | |
| } | |
| } | |
| function setScmSelect(data) { | |
| // 모달에서 공급사 확정 선택 시에만 라디오를 공급사로 변경 | |
| $('input[name="scmFl"][value="y"]').prop('checked', true); | |
| scmFlConfirmed = 'y'; | |
| //공급사 값 세팅 | |
| displayTemplate(data); | |
| toggleMessageDelegateAreaByScm(); | |
| if ($('input[name="permissionSetupMode"]').length != 1) { // 운영자 권한 설정 방식이 아닌 경우(기존) | |
| developmentPermissionToggle(data); | |
| changePermissionLayout(data); | |
변경 전manage_security.php
| <p class="notice-info"> | |
| 로그인한 운영자 정보에 인증정보(SMS, 이메일)가 없는 경우, 보안인증화면이 노출되지 않습니다.<br/>관리자 보안인증 설정을 사용 시, 운영자 정보에 인증정보를 등록해주세요. <a href="../policy/manage_list.php" target="_blank" class="btn-link">운영자 정보 수정하기 ></a> | |
| </p> | |
| <p class="notice-info"> | |
| 인증번호 SMS는 잔여포인트가 있어야 발송됩니다. (잔여포인트 : <?php echo number_format($smsPoint, 1) ?>) <a href="../member/sms_charge.php" target="_blank" class="btn-link">SMS포인트 충전하기 ></a> | |
| </p> | |
| <p class="notice-info"> | |
| SMS잔여포인트가 없는 경우 자동으로 이메일이 인증수단으로 노출되며, 포인트 충전 이후 설정한 인증수단으로 노출됩니다. | |
| </p> | |
| <p class="notice-info"> | |
| 이메일인증은 주문처리 및 프로모션 등으로 인해 이메일이 대량으로 발송될 경우 인증번호 전송이 지연될 수 있으므로 SMS인증을 사용하시길 권장합니다. | |
| </p> | |
| <td> | |
| <input type="hidden" name="excel[smsAuth]" id="excelSmsAuthManager" value="manager" /> | |
| 운영자 정보에 등록된 SMS로 관리자 보안 인증번호가 발송됩니다. | |
| <p class="notice-info">운영자 인증번호 SMS는 잔여포인트가 있어야 발송됩니다. (잔여포인트 : <?= number_format($smsPoint) ?>) <a href="../member/sms_charge.php" target="_blank" class="btn-link">SMS포인트 충전하기 ></a></p> | |
| <p class="notice-info">로그인한 운영자 정보에 SMS 인증정보(휴대폰번호)가 없는 경우, 엑셀 다운로드가 불가합니다.</p> | |
| </td> | |
| </tr> | |
변경 후manage_security.php
| <p class="notice-info"> | |
| 로그인한 운영자 정보에 인증정보(SMS, 이메일)가 없는 경우, 보안인증화면이 노출되지 않습니다.<br/>관리자 보안인증 설정을 사용 시, 운영자 정보에 인증정보를 등록해주세요. <a href="../policy/manage_list.php" target="_blank" class="btn-link">운영자 정보 수정하기 ></a> | |
| </p> | |
| <p class="notice-info"> | |
| 이메일인증은 주문처리 및 프로모션 등으로 인해 이메일이 대량으로 발송될 경우 인증번호 전송이 지연될 수 있으므로 SMS인증을 사용하시길 권장합니다. | |
| </p> | |
| <td> | |
| <input type="hidden" name="excel[smsAuth]" id="excelSmsAuthManager" value="manager" /> | |
| 운영자 정보에 등록된 SMS로 관리자 보안 인증번호가 발송됩니다. | |
| <p class="notice-info">로그인한 운영자 정보에 SMS 인증정보(휴대폰번호)가 없는 경우, 엑셀 다운로드가 불가합니다.</p> | |
| </td> | |
| </tr> | |
변경 전cart_remind_list.php
| <div class="page-header js-affix"> | |
| <h3><?php echo end($naviMenu->location); ?> | |
| </h3> | |
| <input type="hidden" name="smsCallNum" value=""/> | |
| <span class="smsCallNumText"><span class="text-darkred">등록된 SMS 발신번호가 없습니다.</span></span> | |
| <?php if (gd_is_provider() === false) {?> | |
| <a href="<?= $commerceSmsIntroUrl; ?>" target="_blank" class="btn btn-gray btn-sm">발신번호 등록하기</a> | |
| <?php }?> | |
| </div> | |
| <div class="notice-info"> | |
| var params = { | |
| returnInput: 'smsCallNum' | |
| }; | |
| $.get('../member/layer_sms_call_number_select.php', params, function (data) { | |
| BootstrapDialog.show({ | |
| title: 'SMS 발신번호 목록', | |
| message: $(data), | |
| closable: true | |
| }); | |
| }); | |
| }); | |
변경 후cart_remind_list.php
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script> | |
| <div class="page-header js-affix"> | |
| <h3><?php echo end($naviMenu->location); ?> | |
| </h3> | |
| <input type="hidden" name="smsCallNum" value=""/> | |
| <span class="smsCallNumText"><span class="text-darkred">등록된 SMS 발신번호가 없습니다.</span></span> | |
| <?php if (gd_is_provider() === false) {?> | |
| <button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 등록하기</button> | |
| <?php }?> | |
| </div> | |
| <div class="notice-info"> | |
| var params = { | |
| returnInput: 'smsCallNum' | |
| }; | |
| $.get('../member/layer_manage_call_number.php', params, function (data) { | |
| // 200 으로 내려오는 오류는 JSON, 정상 레이어는 HTML 로 응답된다 | |
| if (typeof data === 'object' && data !== null) { | |
| dialog_alert(data.message || '일시적인 오류가 발생하였습니다.<br>다시 시도해 주세요.'); | |
| return; | |
| } | |
| BootstrapDialog.show({ | |
| title: 'SMS 발신번호 목록', | |
| message: $(data), | |
| closable: true | |
| }); | |
| }).fail(function (xhr) { | |
| var response = xhr.responseJSON; | |
| if (response && response.data && response.data.canRegisterRepresentative) { | |
| SmsCallerHubLayer.openRepresentativeRegisterGuide(); | |
| return; | |
| } | |
| if (response && response.data && response.data.isMigrationRequired && response.data.isRepresentative) { | |
| SmsCallerHubLayer.openMigration({ | |
| // 이관을 마치면 막혀 있던 발신번호 관리 레이어를 다시 연다 | |
| onCompleted: function () { | |
| $('.js-sms-call-number').click(); | |
| } | |
| }); | |
| return; | |
| } | |
| // 권한 실패는 403 이라 success 가 아니라 여기로 들어오므로 응답 본문의 안내 문구를 그대로 노출한다 | |
| dialog_alert((response && response.message) || '일시적인 오류가 발생하였습니다.<br>다시 시도해 주세요.'); | |
| }); | |
| }); | |
변경 전comeback_coupon_list.php
| <div class="page-header js-affix"> | |
| <h3><?php echo end($naviMenu->location); ?></h3> | |
| <div class="btn-group"> | |
| <input type="hidden" name="smsCallNum" value=""/> | |
| <span class="smsCallNumText"><span class="text-darkred">등록된 SMS 발신번호가 없습니다.</span></span> | |
| <?php if (gd_is_provider() === false) {?> | |
| <a href="<?= $commerceSmsIntroUrl; ?>" target="_blank" class="btn btn-gray btn-sm">발신번호 등록하기</a> | |
| <?php }?> | |
| </div> | |
| <div class="notice-info"> | |
| var params = { | |
| returnInput: 'smsCallNum' | |
| }; | |
| $.get('../member/layer_sms_call_number_select.php', params, function (data) { | |
| BootstrapDialog.show({ | |
| title: 'SMS 발신번호 목록', | |
| message: $(data), | |
| closable: true | |
| }); | |
| }); | |
| }); | |
변경 후comeback_coupon_list.php
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script> | |
| <div class="page-header js-affix"> | |
| <h3><?php echo end($naviMenu->location); ?></h3> | |
| <div class="btn-group"> | |
| <input type="hidden" name="smsCallNum" value=""/> | |
| <span class="smsCallNumText"><span class="text-darkred">등록된 SMS 발신번호가 없습니다.</span></span> | |
| <?php if (gd_is_provider() === false) {?> | |
| <button type="button" class="btn btn-white btn-sm js-sms-call-number">발신번호 등록하기</button> | |
| <?php }?> | |
| </div> | |
| <div class="notice-info"> | |
| var params = { | |
| returnInput: 'smsCallNum' | |
| }; | |
| $.get('../member/layer_manage_call_number.php', params, function (data) { | |
| // 200 으로 내려오는 오류는 JSON, 정상 레이어는 HTML 로 응답된다 | |
| if (typeof data === 'object' && data !== null) { | |
| dialog_alert(data.message || '일시적인 오류가 발생하였습니다.<br>다시 시도해 주세요.'); | |
| return; | |
| } | |
| BootstrapDialog.show({ | |
| title: 'SMS 발신번호 목록', | |
| message: $(data), | |
| closable: true | |
| }); | |
| }).fail(function (xhr) { | |
| var response = xhr.responseJSON; | |
| if (response && response.data && response.data.canRegisterRepresentative) { | |
| SmsCallerHubLayer.openRepresentativeRegisterGuide(); | |
| return; | |
| } | |
| if (response && response.data && response.data.isMigrationRequired && response.data.isRepresentative) { | |
| SmsCallerHubLayer.openMigration({ | |
| // 이관을 마치면 막혀 있던 발신번호 관리 레이어를 다시 연다 | |
| onCompleted: function () { | |
| $('.js-sms-call-number').click(); | |
| } | |
| }); | |
| return; | |
| } | |
| // 권한 실패는 403 이라 success 가 아니라 여기로 들어오므로 응답 본문의 안내 문구를 그대로 노출한다 | |
| dialog_alert((response && response.message) || '일시적인 오류가 발생하였습니다.<br>다시 시도해 주세요.'); | |
| }); | |
| }); | |
변경 전layer_message_representative_register_guide.php
변경 후layer_message_representative_register_guide.php
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/manager-certification.js') ?>"></script> | |
| <link rel="stylesheet" href="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'css/layer-message-representative-register-guide.css') ?>"/> | |
| <?php $guideAlt = '발신관리책임자 등록 안내 - 우리 쇼핑몰 번호, 도용되지 않게 지켜드려요. 발신관리책임자와 담당자 정보로 발신번호 사용을 관리해 다른 사람이 SMS를 보내지 못하게 막아줘요. 최고운영자만 등록 가능(통합회원과 최고운영자 정보가 동일해야 등록이 가능합니다), 등록은 한 번 1분이면 완료, 26년 10월 25일까지 등록 필수(미등록 시 SMS 관련 기능 이용이 제한됩니다). 공지사항에서 등록 방법, 미등록 시 제한 사항에 대해 자세하게 확인하실 수 있습니다. 급증하는 불법스팸·스미싱 피해 예방을 위한 전기통신사업법 제22조의11 및 전송자격인증 기준 등에 관한 고시(방통위 고시 제2026-1호) 개정에 따른 조치입니다.'; ?> | |
| <div id="popupNoticeRepresentativeRegisterGuide"> | |
| <div class="guide_overlay"></div> | |
| <div class="guide_content"> | |
| <div class="guide_head"> | |
| <p class="tit">발신관리책임자 등록 안내</p> | |
| </div> | |
| <div class="guide_body"> | |
| <?php if (!empty($noticeUri)) { ?> | |
| <a href="<?= $noticeUri ?>" target="_blank" class="guide_notice"> | |
| <img src="/admin/gd_share/img/panel/representative_guide.png" alt="<?= $guideAlt ?>"/> | |
| </a> | |
| <?php } else { ?> | |
| <img src="/admin/gd_share/img/panel/representative_guide.png" alt="<?= $guideAlt ?>"/> | |
| <?php } ?> | |
| <div class="btn_guide_area"> | |
| <button type="button" class="js-representative-register">등록하기</button> | |
| </div> | |
| </div> | |
| <div class="bot_gray_close"> | |
| <input type="checkbox" title="7일동안 보지 않기" onclick="adminPanelCookie('<?= $displayCookie ?>', <?= $displayCookieExpireDay ?>, this);$('#popupNoticeRepresentativeRegisterGuide').hide();"/>7일동안 보지 않기 | |
| <input type="checkbox" title="닫기" onclick="$('#popupNoticeRepresentativeRegisterGuide').hide();"/>닫기 | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| (function () { | |
| var isCommerceConnected = <?= json_encode($isCommerceConnected) ?>; | |
| var isSelfCertified = <?= json_encode($isSelfCertified) ?>; | |
| $('.js-representative-register').on('click', function () { | |
| $('#popupNoticeRepresentativeRegisterGuide').hide(); | |
| // 미연동 최고운영자는 기존 통합회원 연동 프로세스로 보낸다 (복귀 주소는 컨트롤러가 URL 에 담아둔다) | |
| if (!isCommerceConnected) { | |
| const message = '<b>발신관리책임자 등록을 위해 통합회원 연동을 진행합니다.</b><br>통합회원 연동을 한 최고운영자만 발신관리책임자 등록이 가능합니다.'; | |
| const commerceLoginUri = <?= json_encode($commerceLoginUri) ?>; | |
| // 안내를 닫는 모든 경로에서 연동 페이지로 이어져야 한다 | |
| managerCertification.closableAlert(message, '안내', function () { | |
| location.href = commerceLoginUri; | |
| }); | |
| return; | |
| } | |
| // 본인인증 이력이 없으면 인증부터 진행한다 | |
| if (!isSelfCertified) { | |
| managerCertification.certifyWithGuide({onSuccess: checkBlockAndRegister}); | |
| return; | |
| } | |
| // 이미 본인인증(솔루션 diValue 존재)한 운영자는 곧바로 등록 흐름을 탄다 | |
| checkBlockAndRegister(); | |
| }); | |
| // 인증을 마쳐도 차단된 책임자면 등록을 진행시키지 않는다 | |
| function checkBlockAndRegister() { | |
| $.post('/share/message_representative_register_ps.php', {mode: 'checkBlock'}, function (response) { | |
| if (!response || response.success !== true) { | |
| showRequestFailed(response); | |
| return; | |
| } | |
| if (response.data.blocked) { | |
| dialog_alert(response.data.message, '안내'); | |
| return; | |
| } | |
| checkMigrationCaller(); | |
| }, 'json').fail(function () { | |
| showRequestFailed(); | |
| }); | |
| } | |
| // 통합회원 전환 전에 등록해 둔 기존 발신관리책임자가 있으면 이관 여부를 묻는다 | |
| function checkMigrationCaller() { | |
| $.post('/share/message_representative_register_ps.php', {mode: 'checkMigration'}, function (response) { | |
| if (!response || response.success !== true) { | |
| showRequestFailed(response); | |
| return; | |
| } | |
| if (response.data.exists) { | |
| dialog_confirm( | |
| '발신관리책임자 등록이 완료됐습니다.<br>이전에 사용하던 발신번호를 가져오겠습니까?<br>가져오기를 완료해야 새로운 발신번호를 등록할 수 있습니다.', | |
| function (confirmed) { | |
| if (!confirmed) { | |
| showRegisterCompleted(); | |
| return; | |
| } | |
| SmsCallerHubLayer.openMigration({onCompleted: showRegisterCompleted}); | |
| }, | |
| '이전 발신번호 가져오기' | |
| ); | |
| return; | |
| } | |
| showRegisterCompleted(); | |
| }, 'json').fail(function () { | |
| showRequestFailed(); | |
| }); | |
| } | |
| // 응답이 비어 있거나 파싱에 실패하면 백단 문구가 없으므로 기본 문구로 안내한다 | |
| function showRequestFailed(response) { | |
| dialog_alert((response && response.message) || '일시적인 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.', '안내'); | |
| } | |
| // 이관하지 않고 끝나는 경로는 모두 등록 완료를 알린다 | |
| function showRegisterCompleted() { | |
| dialog_alert( | |
| "발신관리책임자 등록이 완료되었습니다.<br>부운영자와 함께 SMS 업무를 하려면 '발신관리위임자'로 설정하거나 'SMS 발송'권한을 부여해 주세요.", | |
| '안내' | |
| ); | |
| } | |
| })(); | |
| </script> | |
변경 전layer_sms_personal_info_access_log.php
변경 후layer_sms_personal_info_access_log.php
| <?php | |
| /* | |
| * Copyright (C) 2026 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| ?> | |
| <style> | |
| .sms-access-log-layer{overflow-y:auto;max-width:600px;max-height:400px} | |
| .sms-access-log-detail{margin:0;padding:0;list-style:none} | |
| .sms-access-log-detail > li{padding:10px 0} | |
| .sms-access-log-detail > li + li{border-top:1px solid #ddd} | |
| .sms-access-log-detail__items{margin:5px 0 0;padding-left:14px;list-style:none} | |
| .sms-access-log-detail__items > li{padding:2px 0} | |
| </style> | |
| <div class="sms-access-log-layer"> | |
| <?php if (!empty($errorMessage)): ?> | |
| <p><?= gd_htmlspecialchars($errorMessage) ?></p> | |
| <?php elseif (!empty($detailRows)): ?> | |
| <ul class="sms-access-log-detail"> | |
| <?php foreach ($detailRows as $row): ?> | |
| <li> | |
| * <?= gd_htmlspecialchars($row['label']) ?><?php if (!empty($row['value'])): ?> : <?= gd_htmlspecialchars($row['value']) ?><?php endif; ?> | |
| <?php if (!empty($row['items'])): ?> | |
| <ul class="sms-access-log-detail__items"> | |
| <?php foreach ($row['items'] as $item): ?> | |
| <li>• <?= gd_htmlspecialchars($item['label']) ?> : <?= gd_htmlspecialchars($item['value']) ?></li> | |
| <?php endforeach; ?> | |
| </ul> | |
| <?php endif; ?> | |
| </li> | |
| <?php endforeach; ?> | |
| </ul> | |
| <?php endif; ?> | |
| </div> | |
변경 전manager_certification_result.php
변경 후manager_certification_result.php
| <script type="text/javascript"> | |
| (function () { | |
| var isCertificated = <?= $isCertificated ? 'true' : 'false' ?>; | |
| var certKey = <?= json_encode($certKey) ?>; | |
| if (window.opener && window.opener.managerCertification) { | |
| window.opener.managerCertification.result(isCertificated, certKey); | |
| } | |
| window.close(); | |
| })(); | |
| </script> | |
변경 전AdminLogDAO.php
| use Framework\Utility\ArrayUtils; | |
| use Framework\Utility\DateTimeUtils; | |
| use Framework\Utility\StringUtils; | |
| /** | |
| * Class AdminLogDAO | |
| case 'register': | |
| case 'modify': | |
| $result['searchConditionMsg'] = '권한 변경 사유 : '. $data['data']['POST']['accessControlReason'] . '<br>'; | |
| $result['searchConditionMsg'] .= '처리 대상 : '. $data['data']['POST']['managerId']; | |
| break; | |
| case 'delete': | |
| } | |
| $data['searchData'] = implode(', ', $managerId); | |
| } | |
| // 암호화 적용 필드 | |
| $encryptTarget = [ | |
| 'email', | |
| $arrBind = []; | |
| if ($request['managerId']) { | |
| $where[] = "a.managerId LIKE concat('%',?,'%') "; | |
| $this->_db->bind_param_push($arrBind, 's', $request['managerId']); | |
| } | |
변경 후AdminLogDAO.php
| use Framework\Utility\ArrayUtils; | |
| use Framework\Utility\DateTimeUtils; | |
| use Framework\Utility\StringUtils; | |
| use Origin\Service\Member\ManagerEditPolicy; | |
| /** | |
| * Class AdminLogDAO | |
| case 'register': | |
| case 'modify': | |
| $result['searchConditionMsg'] = '권한 변경 사유 : '. $data['data']['POST']['accessControlReason'] . '<br>'; | |
| // manage_ps 스냅샷의 휴대폰번호는 적재 시 암호화돼 있어 그대로 출력하면 암호문이 노출된다 | |
| // 적재값은 검증 전 요청 원문이고 상세 화면이 이 문자열을 DOM 으로 파싱하므로 출력 시 이스케이프한다 | |
| $changedCellPhone = ''; | |
| if (!empty($data['data']['isCellPhoneChanged'])) { | |
| $changedCellPhone = gd_htmlspecialchars((string)\Encryptor::decryptJson($data['data']['POST']['cellPhone'])); | |
| } | |
| // 항목을 통째로 빼면 기록을 읽는 쪽이 번호 미변경과 기록 누락을 구분할 수 없다 | |
| $result['searchConditionMsg'] .= '휴대폰번호 : ' . $changedCellPhone . '<br>'; | |
| $result['searchConditionMsg'] .= '처리 대상 : '. $data['data']['POST']['managerId']; | |
| break; | |
| case 'delete': | |
| } | |
| $data['searchData'] = implode(', ', $managerId); | |
| } | |
| $cellPhoneChangeMode = $data['POST']['mode'] ?? null; | |
| $inputCellPhone = (string)($data['POST']['cellPhone'] ?? ''); | |
| // 뒤따르는 암호화 루프가 입력값을 암호문으로 덮으므로 판정은 원문이 남아 있는 이 시점에 끝내야 한다 | |
| if (in_array($cellPhoneChangeMode, ['register', 'modify'], true) && !empty($inputCellPhone)) { | |
| /** @var ManagerEditPolicy $managerEditPolicy */ | |
| $managerEditPolicy = \App::getInstance(ManagerEditPolicy::class); | |
| if ($managerEditPolicy->isCellPhoneChangeApplied((int)($data['POST']['sno'] ?? 0), $inputCellPhone)) { | |
| $data['isCellPhoneChanged'] = true; | |
| } | |
| } | |
| // 암호화 적용 필드 | |
| $encryptTarget = [ | |
| 'email', | |
| $arrBind = []; | |
| if ($request['managerId']) { | |
| // 개인정보 과다 노출 방지를 위해 부분포함이 아닌 전체일치로만 조회한다 | |
| $where[] = "a.managerId = ? "; | |
| $this->_db->bind_param_push($arrBind, 's', $request['managerId']); | |
| } | |
변경 전AdminMenu.php
| 'memberHack' => '회원탈퇴', | |
| 'memberExcelDown' => '엑셀다운로드', | |
| 'memberMaskingUseFl' => '개인정보조회 제한', | |
| ], | |
| 'godo00176' => [ | |
| 'boardDelete' => '게시글 삭제', | |
| $this->db->bind_param_push($this->arrBind, 's', $subMenu); | |
| $this->db->bind_param_push($this->arrBind, 's', $lastMenu); | |
| $this->db->strField = 't.adminMenuNo'; | |
| $join[] = 'LEFT JOIN ' . DB_ADMIN_MENU . ' as s ON s.adminMenuDepth = 2 AND f.adminMenuNo = s.adminMenuParentNo'; | |
| $join[] = 'LEFT JOIN ' . DB_ADMIN_MENU . ' as t ON t.adminMenuDepth = 3 AND s.adminMenuNo = t.adminMenuParentNo'; | |
| $this->db->strJoin = implode(' ', $join); | |
| $this->db->strWhere = implode(' AND ', $this->arrWhere); | |
| $query = $this->db->query_complete(); | |
| $strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . DB_ADMIN_MENU . ' as f ' . implode(' ', $query); | |
| $menuData = $this->db->query_fetch($strSQL, $this->arrBind, false); | |
| unset($this->arrBind); | |
| unset($this->arrWhere); | |
| return $menuData['adminMenuNo']; | |
| } | |
| /** | |
변경 후AdminMenu.php
| 'memberHack' => '회원탈퇴', | |
| 'memberExcelDown' => '엑셀다운로드', | |
| 'memberMaskingUseFl' => '개인정보조회 제한', | |
| 'messageSendSms' => 'SMS 발송', | |
| ], | |
| 'godo00176' => [ | |
| 'boardDelete' => '게시글 삭제', | |
| $this->db->bind_param_push($this->arrBind, 's', $subMenu); | |
| $this->db->bind_param_push($this->arrBind, 's', $lastMenu); | |
| $this->db->strField = 't.adminMenuNo, t.adminMenuType'; | |
| $join[] = 'LEFT JOIN ' . DB_ADMIN_MENU . ' as s ON s.adminMenuDepth = 2 AND f.adminMenuNo = s.adminMenuParentNo'; | |
| $join[] = 'LEFT JOIN ' . DB_ADMIN_MENU . ' as t ON t.adminMenuDepth = 3 AND s.adminMenuNo = t.adminMenuParentNo'; | |
| $this->db->strJoin = implode(' ', $join); | |
| $this->db->strWhere = implode(' AND ', $this->arrWhere); | |
| $query = $this->db->query_complete(); | |
| $strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . DB_ADMIN_MENU . ' as f ' . implode(' ', $query); | |
| $menuList = $this->db->query_fetch($strSQL, $this->arrBind); | |
| unset($this->arrBind); | |
| unset($this->arrWhere); | |
| $adminMenuType = gd_is_provider() && self::isProviderDirectory() ? 's' : 'd'; | |
| return $this->pickAdminMenuNoByAdminMenuType($menuList, $adminMenuType); | |
| } | |
| /** | |
| * 본사(d)·공급사(s) 에 같은 3단 코드가 존재하는 메뉴는 조회 결과가 여러 행이 된다. | |
| * 한쪽 유형만 등록된 메뉴가 0행으로 사라지지 않게 유형 조건은 SQL 에 넣지 않고 여기서 고른다. | |
| * | |
| * @param array $menuList [['adminMenuNo' => string, 'adminMenuType' => string], ...] | |
| * @param string $adminMenuType 본사(d)/공급사(s) | |
| * @return string | |
| */ | |
| protected function pickAdminMenuNoByAdminMenuType(array $menuList, string $adminMenuType): string | |
| { | |
| // 메뉴 행이 없으면 빈 menuCode 로 진행해 TypeError 500 대신 admin API 401 안내 경로를 타게 한다 | |
| if (empty($menuList)) { | |
| return ''; | |
| } | |
| foreach ($menuList as $menuData) { | |
| if ($menuData['adminMenuType'] === $adminMenuType) { | |
| return $menuData['adminMenuNo']; | |
| } | |
| } | |
| return $menuList[0]['adminMenuNo']; | |
| } | |
| /** | |
변경 전CartRemind.php
| use Framework\Utility\ArrayUtils; | |
| use Framework\Utility\DateTimeUtils; | |
| use Framework\Utility\GodoUtils; | |
| use Session; | |
| class CartRemind | |
| // 전송실패 수 | |
| $result['fail'] = 0; | |
| foreach ($cartRemindSendData['member'] as $memKey => $memVal) { | |
| // 장바구니 알림에 쿠폰 지급이 있다면 지급 | |
| if ($cartRemindSendData['coupon'] > 0) { | |
| return $result; | |
| } | |
| /** | |
| * 장바구니 알림 삭제 (이미 발송 등록된 SMS 는 삭제 되지 않음) | |
| * | |
변경 후CartRemind.php
| use Framework\Utility\ArrayUtils; | |
| use Framework\Utility\DateTimeUtils; | |
| use Framework\Utility\GodoUtils; | |
| use Origin\Enum\ApiClient\Notification\SmsPersonalInfoAccessLogEventType; | |
| use Session; | |
| class CartRemind | |
| // 전송실패 수 | |
| $result['fail'] = 0; | |
| // 개인정보접속기록 발행은 발송 지점(SmsAdmin::sendSms)에서 한다 | |
| // 튜닝 상점의 구 버전 복제본에는 없는 신규 메서드라 존재 확인 후 호출한다 | |
| if (method_exists($smsAdmin, 'setPublishPersonalInfoAccessLog') && method_exists($smsAdmin, 'setPersonalInfoAccessLogEventType')) { | |
| $smsAdmin->setPublishPersonalInfoAccessLog(true); | |
| $smsAdmin->setPersonalInfoAccessLogEventType($this->resolveAccessLogEventType($cartRemindSendData['type'])); | |
| } else { | |
| \Logger::channel('sms')->warning('튜닝으로 인한 개인정보접속기록 발행 설정 미적용', ['missingMethod' => 'SmsAdmin::setPublishPersonalInfoAccessLog, SmsAdmin::setPersonalInfoAccessLogEventType']); | |
| } | |
| foreach ($cartRemindSendData['member'] as $memKey => $memVal) { | |
| // 장바구니 알림에 쿠폰 지급이 있다면 지급 | |
| if ($cartRemindSendData['coupon'] > 0) { | |
| return $result; | |
| } | |
| /** | |
| * @param string $sendType 'auto' 면 크론 자동 발송, 그 외는 관리자 즉시발송 | |
| * @return string | |
| */ | |
| protected function resolveAccessLogEventType($sendType) | |
| { | |
| return $sendType === 'auto' | |
| ? SmsPersonalInfoAccessLogEventType::SMS_SEND_AUTO | |
| : SmsPersonalInfoAccessLogEventType::SMS_SEND_MANUAL; | |
| } | |
| /** | |
| * 장바구니 알림 삭제 (이미 발송 등록된 SMS 는 삭제 되지 않음) | |
| * | |
변경 전CouponAdmin.php
| use Framework\Utility\ArrayUtils; | |
| use Framework\Utility\ProducerUtils; | |
| use Globals; | |
| use Request; | |
| use Session; | |
| use UserFilePath; | |
| if ($getData['smsFl'] == 'y') { | |
| $smsSendData['password'] = $request->post()->get('password'); | |
| $smsSendData['passwordCheckFl'] = $request->post()->get('passwordCheckFl'); | |
| $cnt = $smsAdmin->sendSms($smsSendData); | |
| $logger->info(sprintf(__('SMS 발송이 완료되었습니다. (%1$s건 성공 / %2$s건 실패)'), $cnt['success'], $cnt['fail'])); | |
| } | |
변경 후CouponAdmin.php
| use Framework\Utility\ArrayUtils; | |
| use Framework\Utility\ProducerUtils; | |
| use Globals; | |
| use Origin\Enum\ApiClient\Notification\SmsPersonalInfoAccessLogEventType; | |
| use Request; | |
| use Session; | |
| use UserFilePath; | |
| if ($getData['smsFl'] == 'y') { | |
| $smsSendData['password'] = $request->post()->get('password'); | |
| $smsSendData['passwordCheckFl'] = $request->post()->get('passwordCheckFl'); | |
| // 개인정보접속기록 발행은 발송 지점(SmsAdmin::sendSms)에서 한다 | |
| // 튜닝 상점의 구 버전 복제본에는 없는 신규 메서드라 존재 확인 후 호출한다 | |
| if (method_exists($smsAdmin, 'setPublishPersonalInfoAccessLog') && method_exists($smsAdmin, 'setPersonalInfoAccessLogEventType')) { | |
| $smsAdmin->setPublishPersonalInfoAccessLog(true); | |
| $smsAdmin->setPersonalInfoAccessLogEventType(SmsPersonalInfoAccessLogEventType::SMS_SEND_MANUAL); | |
| } else { | |
| \Logger::channel('sms')->warning('튜닝으로 인한 개인정보접속기록 발행 설정 미적용', ['missingMethod' => 'SmsAdmin::setPublishPersonalInfoAccessLog, SmsAdmin::setPersonalInfoAccessLogEventType']); | |
| } | |
| $cnt = $smsAdmin->sendSms($smsSendData); | |
| $logger->info(sprintf(__('SMS 발송이 완료되었습니다. (%1$s건 성공 / %2$s건 실패)'), $cnt['success'], $cnt['fail'])); | |
| } | |
변경 전DBTableField.php
| ['val' => 'isOrderSearchMultiGrid', 'typ' => 's', 'def' => 'n'], //주문 멀티검색여부 | |
| ['val' => 'ipManagerSecurityFl', 'typ' => 's', 'def' => 'n'], //관리자 ip접속제한 사용여부 | |
| ['val' => 'commerceId', 'typ' => 's', 'def' => null], // 커머스 아이디 | |
| ]; | |
| // @formatter:on | |
변경 후DBTableField.php
| ['val' => 'isOrderSearchMultiGrid', 'typ' => 's', 'def' => 'n'], //주문 멀티검색여부 | |
| ['val' => 'ipManagerSecurityFl', 'typ' => 's', 'def' => 'n'], //관리자 ip접속제한 사용여부 | |
| ['val' => 'commerceId', 'typ' => 's', 'def' => null], // 커머스 아이디 | |
| ['val' => 'diValue', 'typ' => 's', 'def' => null], // 중복가입확인정보(DI) | |
| ['val' => 'messageRoleType', 'typ' => 's', 'def' => null], // 발신관리위임자 역할(위임자: delegate, 미지정: null) | |
| ]; | |
| // @formatter:on | |
변경 전ManagerCs.php
| } | |
| unset($funcs); | |
| if (!$isSuper) { | |
| unset($result['goodsStockExceptView']); // 상품 상세 재고 수정 제외 | |
| unset($result['orderExcelDown']); // 주문/배송 엑셀 다운로드 | |
변경 후ManagerCs.php
| } | |
| unset($funcs); | |
| unset($result['messageSendSms']); | |
| if (!$isSuper) { | |
| unset($result['goodsStockExceptView']); // 상품 상세 재고 수정 제외 | |
| unset($result['orderExcelDown']); // 주문/배송 엑셀 다운로드 | |
변경 전Manager.php
| use Component\Member\Util\MemberUtil; | |
| use Component\Policy\ManageSecurityPolicy; | |
| use Component\Sms\Sms; | |
| use Component\Sms\SmsMessage; | |
| use Component\Sms\SmsSender; | |
| use Component\Storage\Storage; | |
| use Component\Validator\Validator; | |
| use Encryptor; | |
| use Request; | |
| use Session; | |
| use Origin\Service\Admin\Menu\AdminMenuFlagService; | |
| /** | |
| * Class Manager | |
| $this->search['isSuper'] = $req['isSuper']; | |
| $this->search['noVisitFl'] = gd_isset($req['noVisitFl'], 'n'); | |
| $this->search['searchKind'] = gd_isset($req['searchKind']); | |
| $this->checked['employeeFl'][$this->search['employeeFl']] = | |
| $this->checked['smsAutoReceive'][$this->search['smsAutoReceive']] = | |
| $this->checked['scmFl'][$this->search['scmFl']] = 'checked = "checked"'; | |
| // 키워드 검색 | |
| if ($this->search['keyword']) { | |
| $this->checked['smsAutoReceive']['all'] = 'checked'; | |
| } | |
| // 부서 검색 | |
| if ($this->search['departmentCd']) { | |
| $this->arrWhere[] = 'departmentCd = ?'; | |
| $validator = new Validator(); | |
| $arrData['memo'] = strip_tags($arrData['memo']);//관리자 메모 XSS방지 | |
| if ($checkMode === 'modify') { | |
| $managerData = $this->getManagerInfo($arrData['sno']); | |
| if (empty($arrData['functionAuth']['goodsStockExceptView']) === false) { | |
| $functionAuth['functionAuth']['goodsStockExceptView'] = $arrData['functionAuth']['goodsStockExceptView']; | |
| } | |
| } | |
| $arrData['permissionMenu'] = json_encode($permission, JSON_UNESCAPED_UNICODE); // 운영자 접근 권한 설정 | |
| $arrData['functionAuth'] = json_encode($functionAuth, JSON_UNESCAPED_UNICODE); // 운영자 기능 권한 설정 | |
| $validator->add('permissionMenu', ''); // 운영자 권한 - 일부 권한 일 경우 허용된 메뉴 접근 권한 | |
| $validator->add('functionAuth', ''); // 운영자 권한 - 일부 권한 일 경우 허용된 기능 권한 | |
| $validator->add('isSmsAuth', 'yn'); // 휴대폰인증여부 | |
| $validator->add('isEmailAuth', 'yn'); // 이메일인증여부 | |
| $validator->add('memo', ''); // 메모 | |
| $validator->add('ipManagerSecurityFl', 'yn', true); // 운영자 ip 접속제한 설정여부 | |
| if (empty($arrData['functionAuth']['boardMaskingUseFl']) === false) { | |
| $functionAuth['functionAuth']['boardMaskingUseFl'] = $arrData['functionAuth']['boardMaskingUseFl']; | |
| } | |
| } | |
| $arrData['permissionMenu'] = json_encode($permission, JSON_UNESCAPED_UNICODE); // 운영자 접근 권한 설정 | |
| $arrData['writeEnabledMenu'] = json_encode($writeEnabledMenu, JSON_UNESCAPED_UNICODE); // 운영자 쓰기 권한 설정 | |
| $managersData[] = $data; | |
| } | |
| // 운영자 권한 저장 | |
| foreach ($managersData as $data) { | |
| $arrManager = []; | |
| foreach ($functionList as $lists) { | |
| foreach ($lists as $code => $name) { | |
| if ($code == 'goodsStockExceptView') continue; | |
| $data['functionAuth'][$code] = 'y'; | |
| } | |
| } | |
| throw new ManagerAuthException(ManagerAuthException::CODE_NOT_EQUALS_CELLPHONE); | |
| } | |
| $arrData['isSmsAuth'] = 'y'; | |
| } elseif ($arrData['isSmsAuth'] === 'y') { | |
| unset($arrData['cellPhone']); | |
| } else { | |
| } | |
| } | |
| $strSql = "UPDATE " . DB_MANAGER . " SET isDelete = 'y' WHERE sno = ?"; | |
| $this->db->bind_query( | |
| $strSql, | |
| [ | |
| */ | |
| public function sendSmsAuthNumber($managerInfo) | |
| { | |
| $smsPoint = Sms::getPoint(); | |
| if ($smsPoint >= 1) { | |
| if (!$managerInfo['cellPhone']) { | |
| throw new \Exception(__('관리자 보안을 위해 휴대폰 인증 로그인을 사용중입니다. 로그인하신 관리 아이디는 인증된 휴대폰번호가 없어 로그인할 수 없으니, 대표운영자에게 문의하시기 바랍니다.')); | |
| } | |
| // 인증번호 생성 | |
| $adminSecuritySmsAuthNumber = Otp::getOtp(8); | |
| $contents = sprintf(__('관리자님 인증번호는 [%s] 입니다.'), $adminSecuritySmsAuthNumber); | |
| $receiver[]['cellPhone'] = $managerInfo['cellPhone']; | |
| $smsSender = \App::load(SmsSender::class); | |
| $smsSender->validPassword(\App::load(\Component\Sms\SmsUtil::class)->getPassword()); | |
| $smsSender->setSmsPoint($smsPoint); | |
| $smsSender->setMessage(new SmsMessage($contents)); | |
| $smsSender->setSmsType('user'); | |
| $smsSender->setMsgType('auth'); //인증용 | |
| $smsSender->setReceiver($receiver); | |
| $smsSender->setLogData(['disableResend' => true]); | |
| $smsSender->setContentsMask([$adminSecuritySmsAuthNumber]); | |
| $smsResult = $smsSender->send(); | |
| $smsAuth['smsAuthNumber'] = $adminSecuritySmsAuthNumber; | |
| if ($smsResult['success'] === 1) { | |
| $smsAuth['message'] = 'OK'; | |
| } else { | |
| $smsAuth['message'] = 'SMS Send Fail'; | |
| } | |
| } else { | |
| $smsAuth['message'] = 'SMS Point Fail'; | |
| } | |
| return $smsAuth; | |
| } | |
변경 후Manager.php
| use Component\Member\Util\MemberUtil; | |
| use Component\Policy\ManageSecurityPolicy; | |
| use Component\Sms\Sms; | |
| use Component\Storage\Storage; | |
| use Component\Validator\Validator; | |
| use Encryptor; | |
| use Request; | |
| use Session; | |
| use Origin\Service\Admin\Menu\AdminMenuFlagService; | |
| use Origin\Enum\Crm\Message\MessageOperatorRoleType; | |
| use Origin\Service\Crm\Message\MessageOperatorRoleGuard; | |
| use Origin\Service\Crm\Message\MessageOperatorRoleService; | |
| use Origin\Service\Member\ManagerEditPolicy; | |
| use Origin\Service\Member\ManagerService; | |
| use Origin\Service\Sms\OccupancyAuth\OccupancyOtpSmsService; | |
| /** | |
| * Class Manager | |
| $this->search['isSuper'] = $req['isSuper']; | |
| $this->search['noVisitFl'] = gd_isset($req['noVisitFl'], 'n'); | |
| $this->search['searchKind'] = gd_isset($req['searchKind']); | |
| $this->search['smsRole'] = gd_isset($req['smsRole'], 'all'); | |
| $this->checked['employeeFl'][$this->search['employeeFl']] = 'checked = "checked"'; | |
| $this->checked['smsAutoReceive'][$this->search['smsAutoReceive']] = 'checked = "checked"'; | |
| $this->checked['scmFl'][$this->search['scmFl']] = 'checked = "checked"'; | |
| $this->checked['smsRole'][$this->search['smsRole']] = 'checked="checked"'; | |
| // 키워드 검색 | |
| if ($this->search['keyword']) { | |
| $this->checked['smsAutoReceive']['all'] = 'checked'; | |
| } | |
| // SMS 권한 검색 (발신관리책임자=등록 완료 최고운영자 sno=1 / 발신관리위임자=messageRole / 발송담당자=functionAuth.messageSendSms 보유) | |
| switch ($this->search['smsRole']) { | |
| case MessageOperatorRoleType::REPRESENTATIVE: | |
| // 통합회원 연동(commerceId)·본인인증(diValue) 미완료 최고운영자는 역할이 없어 검색 결과에서 빠진다 | |
| $this->arrWhere[] = "m.sno = ? AND IFNULL(m.commerceId, '') != '' AND IFNULL(m.diValue, '') != ''"; | |
| $this->db->bind_param_push($this->arrBind, 'i', ManagerService::SUPER_MANAGER_SNO); | |
| break; | |
| case MessageOperatorRoleType::DELEGATE: | |
| $this->arrWhere[] = 'm.messageRoleType = ?'; | |
| $this->db->bind_param_push($this->arrBind, 's', MessageOperatorRoleType::DELEGATE); | |
| break; | |
| case MessageOperatorRoleType::SENDER: | |
| // functionAuth JSON 저장 시 공백 유무가 경로마다 달라 JSON 경로로 판정 | |
| $this->arrWhere[] = "JSON_VALID(m.functionAuth) AND JSON_UNQUOTE(JSON_EXTRACT(m.functionAuth, '$.functionAuth.messageSendSms')) = ? AND m.sno != ? AND IFNULL(m.messageRoleType, '') != ?"; | |
| $this->db->bind_param_push($this->arrBind, 's', 'y'); | |
| $this->db->bind_param_push($this->arrBind, 'i', ManagerService::SUPER_MANAGER_SNO); | |
| $this->db->bind_param_push($this->arrBind, 's', MessageOperatorRoleType::DELEGATE); | |
| break; | |
| } | |
| // 부서 검색 | |
| if ($this->search['departmentCd']) { | |
| $this->arrWhere[] = 'departmentCd = ?'; | |
| $validator = new Validator(); | |
| $arrData['memo'] = strip_tags($arrData['memo']);//관리자 메모 XSS방지 | |
| // 본인인증 결과(DI)는 인증 경로만 기록·파기한다 — 요청값이 그대로 실려 저장되지 않도록 여기서 걷어낸다 | |
| unset($arrData['diValue']); | |
| if ($checkMode === 'modify') { | |
| $managerData = $this->getManagerInfo($arrData['sno']); | |
| if (empty($arrData['functionAuth']['goodsStockExceptView']) === false) { | |
| $functionAuth['functionAuth']['goodsStockExceptView'] = $arrData['functionAuth']['goodsStockExceptView']; | |
| } | |
| if (!empty($arrData['functionAuth']['messageSendSms'])) { | |
| $functionAuth['functionAuth']['messageSendSms'] = $arrData['functionAuth']['messageSendSms']; | |
| } | |
| } | |
| // 최고운영자·위임자는 발송담당자-SMS 권한이 강제(체크박스 disabled 라 폼 미전송)되므로 저장 시 보존 | |
| /** @var MessageOperatorRoleService $messageOperatorRoleService */ | |
| $messageOperatorRoleService = \App::getInstance(MessageOperatorRoleService::class); | |
| if (!empty($arrData['sno']) && $messageOperatorRoleService->isSenderPermissionLocked((int)$arrData['sno'])) { | |
| $functionAuth['functionAuth']['messageSendSms'] = 'y'; | |
| } | |
| $arrData['permissionMenu'] = json_encode($permission, JSON_UNESCAPED_UNICODE); // 운영자 접근 권한 설정 | |
| $arrData['functionAuth'] = json_encode($functionAuth, JSON_UNESCAPED_UNICODE); // 운영자 기능 권한 설정 | |
| $validator->add('permissionMenu', ''); // 운영자 권한 - 일부 권한 일 경우 허용된 메뉴 접근 권한 | |
| $validator->add('functionAuth', ''); // 운영자 권한 - 일부 권한 일 경우 허용된 기능 권한 | |
| $validator->add('isSmsAuth', 'yn'); // 휴대폰인증여부 | |
| // 미등록 항목은 act() 의 garbage delete 로 사라져 파기가 저장에 반영되지 않는다 | |
| $validator->add('diValue', ''); // 중복가입확인정보(DI) | |
| $validator->add('isEmailAuth', 'yn'); // 이메일인증여부 | |
| $validator->add('memo', ''); // 메모 | |
| $validator->add('ipManagerSecurityFl', 'yn', true); // 운영자 ip 접속제한 설정여부 | |
| if (empty($arrData['functionAuth']['boardMaskingUseFl']) === false) { | |
| $functionAuth['functionAuth']['boardMaskingUseFl'] = $arrData['functionAuth']['boardMaskingUseFl']; | |
| } | |
| if (!empty($arrData['functionAuth']['messageSendSms'])) { | |
| $functionAuth['functionAuth']['messageSendSms'] = $arrData['functionAuth']['messageSendSms']; | |
| } | |
| } | |
| // 최고운영자·위임자는 발송담당자-SMS 권한이 강제(체크박스 disabled 라 폼 미전송)되므로 저장 시 보존 | |
| /** @var MessageOperatorRoleService $messageOperatorRoleService */ | |
| $messageOperatorRoleService = \App::getInstance(MessageOperatorRoleService::class); | |
| if (!empty($arrData['sno']) && $messageOperatorRoleService->isSenderPermissionLocked((int)$arrData['sno'])) { | |
| $functionAuth['functionAuth']['messageSendSms'] = 'y'; | |
| } | |
| $arrData['permissionMenu'] = json_encode($permission, JSON_UNESCAPED_UNICODE); // 운영자 접근 권한 설정 | |
| $arrData['writeEnabledMenu'] = json_encode($writeEnabledMenu, JSON_UNESCAPED_UNICODE); // 운영자 쓰기 권한 설정 | |
| $managersData[] = $data; | |
| } | |
| // SMS 발송담당자(messageSendSms) 권한 변경 검증 — 벌크 대상 합산 (클라이언트 submitHandler 와 이중 방어) | |
| $smsRoleService = \App::getInstance(MessageOperatorRoleService::class); | |
| $smsRoleGuard = \App::getInstance(MessageOperatorRoleGuard::class); | |
| $smsGranted = is_array($arrData['functionAuth']) && (($arrData['functionAuth'][MessageOperatorRoleService::SEND_SMS_FUNCTION_AUTH] ?? '') === 'y'); | |
| $smsChanged = false; | |
| $smsNewSenderCount = 0; | |
| foreach ($managersData as $smsTarget) { | |
| // 최고운영자(sno=1)/위임자는 messageSendSms 강제(잠금)라 변경 대상 아님 | |
| if ($smsRoleService->isSenderPermissionLocked((int)$smsTarget['sno'])) { | |
| continue; | |
| } | |
| $smsNowGranted = $smsRoleService->hasSenderPermission((int)$smsTarget['sno']); | |
| // CS 계정은 발송담당자로 지정할 수 없음 (신규 부여 차단) | |
| if ($smsGranted && $smsNowGranted === false && ($smsTarget['isSuper'] ?? 'n') === 'cs') { | |
| throw new Exception(__('CS 계정은 발송담당자로 지정할 수 없습니다.')); | |
| } | |
| if ($smsNowGranted !== $smsGranted) { | |
| $smsChanged = true; | |
| } | |
| if ($smsGranted && $smsNowGranted === false) { | |
| $smsNewSenderCount++; // 신규로 발송담당자가 되는 대상 | |
| } | |
| } | |
| if ($smsChanged) { | |
| if ($smsRoleGuard->isDelegateOrHigher() === false) { | |
| throw new Exception(__('SMS 발송 권한은 발신관리책임자 또는 발신관리위임자만 부여할 수 있습니다.<br>해당 권한을 해제한 후 다시 저장해 주세요.')); | |
| } | |
| if ($smsRoleGuard->isRepresentative() && $smsRoleService->isBlockedRepresentative()) { | |
| throw new Exception(__('SMS 발송 메뉴 접근이 제한되었습니다.<br>스팸 또는 부정 사용으로 차단되어 접근할 수 없습니다.<br>자세한 사항은 1:1문의로 문의하시기 바랍니다.')); | |
| } | |
| if ($smsGranted && ($smsRoleService->countSenders() + $smsNewSenderCount) > MessageOperatorRoleService::MAX_SENDER_COUNT) { | |
| throw new Exception(__('발송담당자는 최대 %d명까지 지정 가능합니다.', MessageOperatorRoleService::MAX_SENDER_COUNT)); | |
| } | |
| if (trim((string)($arrData['accessControlReason'] ?? '')) === '') { | |
| throw new Exception(__('발송담당자 권한 변경 사유를 작성해주세요.')); | |
| } | |
| } | |
| // 운영자 권한 저장 | |
| foreach ($managersData as $data) { | |
| $arrManager = []; | |
| foreach ($functionList as $lists) { | |
| foreach ($lists as $code => $name) { | |
| if ($code == 'goodsStockExceptView') continue; | |
| if ($code == MessageOperatorRoleService::SEND_SMS_FUNCTION_AUTH) continue; // SMS 발송담당자는 통제 권한이라 신규 기본 부여 제외 | |
| $data['functionAuth'][$code] = 'y'; | |
| } | |
| } | |
| throw new ManagerAuthException(ManagerAuthException::CODE_NOT_EQUALS_CELLPHONE); | |
| } | |
| $arrData['isSmsAuth'] = 'y'; | |
| /** @var ManagerEditPolicy $managerEditPolicy */ | |
| $managerEditPolicy = \App::getInstance(ManagerEditPolicy::class); | |
| $targetSno = (int)($arrData['sno'] ?? 0); | |
| $cellPhone = (string)($arrData['cellPhone'] ?? ''); | |
| if ($managerEditPolicy->isCertifiedCellPhoneReplaced($targetSno, $cellPhone)) { | |
| // 점유인증으로 번호를 교체하면 기존 DI 는 이전 번호로 받은 인증 결과라 남겨 둘 수 없다 | |
| $arrData['diValue'] = null; | |
| } | |
| } elseif ($arrData['isSmsAuth'] === 'y') { | |
| unset($arrData['cellPhone']); | |
| } else { | |
| } | |
| } | |
| // 삭제된 계정에 본인확인 식별자를 남겨 두지 않는다 — 점유인증 여부(isSmsAuth)는 번호 보유 사실이라 유지한다 | |
| // NULL 은 값이 아니라 키워드라 바인드로 표현할 수 없다 — bind_param 이 값마다 escape 를 걸어 빈 문자열이 된다 | |
| $strSql = "UPDATE " . DB_MANAGER . " SET isDelete = 'y', diValue = NULL WHERE sno = ?"; | |
| $this->db->bind_query( | |
| $strSql, | |
| [ | |
| */ | |
| public function sendSmsAuthNumber($managerInfo) | |
| { | |
| if (!$managerInfo['cellPhone']) { | |
| throw new \Exception(__('관리자 보안을 위해 휴대폰 인증 로그인을 사용중입니다. 로그인하신 관리 아이디는 인증된 휴대폰번호가 없어 로그인할 수 없으니, 대표운영자에게 문의하시기 바랍니다.')); | |
| } | |
| $adminSecuritySmsAuthNumber = Otp::getOtp(8); | |
| $contents = sprintf(__('관리자님 인증번호는 [%s] 입니다.'), $adminSecuritySmsAuthNumber); | |
| $isSent = \App::getInstance(OccupancyOtpSmsService::class)->send($managerInfo['cellPhone'], $contents, $adminSecuritySmsAuthNumber); | |
| $smsAuth['smsAuthNumber'] = $adminSecuritySmsAuthNumber; | |
| $smsAuth['message'] = $isSent ? 'OK' : 'SMS Send Fail'; | |
| return $smsAuth; | |
| } | |
변경 전SmsAdmin.php
| use Framework\Utility\StringUtils; | |
| use Respect\Validation\Validator; | |
| use Origin\Service\Member\CrmGroupService; | |
| use Origin\Enum\ManagerTutorialType; | |
| use Origin\Service\Admin\Member\ManagerTutorial\ManagerTutorialTopicService; | |
| /** | |
| * SMS 관리자 관련 처리 | |
| class SmsAdmin | |
| { | |
| /** @var \Framework\Database\DBTool $db */ | |
| private $db; | |
| /** @var SmsAutoCode $smsAutoCode */ | |
| protected $smsAutoCode; | |
| protected $recall; | |
| protected $fieldTypes; | |
| const ALPHA_SMS_POINT_LIST = 'https://alpha-www.nhn-commerce.com/userinterface/_godoConn/conf/sms.cfg'; | |
| const REAL_SMS_POINT_LIST = 'https://www.nhn-commerce.com/userinterface/_godoConn/conf/sms.cfg'; | |
| } | |
| } | |
| // SMS 기본 정보 저장 | |
| $config['smsCallNum'] = $request['smsCallNum']; | |
| $this->setConfig($config); | |
| // 기 저장된 항목 삭제 | |
| unset($request['mode'], $request['smsCallNum']); | |
| return $setData; | |
| } | |
| /** | |
| * 관리자 SMS 개별/전체 발송 | |
| * | |
| // 예약 일자 (발송시간) | |
| $smsSendDate = date('Y-m-d H:i:s', $smsSendReserveDate); | |
| // 발송 타입 | |
| $tranType = 'res_send'; | |
| } else { | |
| // 발송시간 | |
| $smsSendDate = date('Y-m-d H:i:s', time()); | |
| // 발송 타입 | |
| $tranType = 'send'; | |
| // SMS 발송 | |
| $cnt = $transport->send(); | |
| // excel전송일경우 DB_SMS_EXCEL_LOG 테이블정보 삭제 | |
| if ((int) $cnt['success'] > 0 && $receiverData['receiverType'] === 'excel') { | |
변경 후SmsAdmin.php
| use Framework\Utility\StringUtils; | |
| use Respect\Validation\Validator; | |
| use Origin\Service\Member\CrmGroupService; | |
| use Origin\Enum\ApiClient\Notification\SmsPersonalInfoAccessLogEventType; | |
| use Origin\Enum\ManagerTutorialType; | |
| use Origin\Service\Admin\Member\ManagerTutorial\ManagerTutorialTopicService; | |
| use Origin\Service\Crm\Message\SmsPersonalInfoAccessLogService; | |
| /** | |
| * SMS 관리자 관련 처리 | |
| class SmsAdmin | |
| { | |
| /** @var \Framework\Database\DBTool $db */ | |
| protected $db; | |
| /** @var SmsAutoCode $smsAutoCode */ | |
| protected $smsAutoCode; | |
| protected $recall; | |
| protected $fieldTypes; | |
| // sendSms() 는 CartRemind·CouponAdmin 등 발행 대상이 아닌 경로에서도 호출되므로 수동발송 경로만 opt-in 으로 발행한다 | |
| protected $publishPersonalInfoAccessLog = false; | |
| protected $personalInfoAccessLogEventType = SmsPersonalInfoAccessLogEventType::SMS_SEND_MANUAL; | |
| // CLI(스케줄러) 세션은 SessionManager 가 아니라 발행 서비스 생성이 매번 실패하고 수신대상 루프 안에서 호출돼, 첫 실패 후에는 같은 프로세스에서 재시도·재로그하지 않는다 | |
| protected static $personalInfoAccessLogPublishFailed = false; | |
| const ALPHA_SMS_POINT_LIST = 'https://alpha-www.nhn-commerce.com/userinterface/_godoConn/conf/sms.cfg'; | |
| const REAL_SMS_POINT_LIST = 'https://www.nhn-commerce.com/userinterface/_godoConn/conf/sms.cfg'; | |
| } | |
| } | |
| // 기 저장된 항목 삭제 | |
| unset($request['mode'], $request['smsCallNum']); | |
| return $setData; | |
| } | |
| /** | |
| * @param bool $publishPersonalInfoAccessLog 수동 발송 개인정보접속기록 발행 여부 | |
| */ | |
| public function setPublishPersonalInfoAccessLog(bool $publishPersonalInfoAccessLog) | |
| { | |
| $this->publishPersonalInfoAccessLog = $publishPersonalInfoAccessLog; | |
| } | |
| /** | |
| * @param string $personalInfoAccessLogEventType 발행할 개인정보접속기록 이벤트 유형 | |
| */ | |
| public function setPersonalInfoAccessLogEventType(string $personalInfoAccessLogEventType) | |
| { | |
| $this->personalInfoAccessLogEventType = $personalInfoAccessLogEventType; | |
| } | |
| /** | |
| * 관리자 SMS 개별/전체 발송 | |
| * | |
| // 예약 일자 (발송시간) | |
| $smsSendDate = date('Y-m-d H:i:s', $smsSendReserveDate); | |
| $reservedAt = $smsSendDate; | |
| // 발송 타입 | |
| $tranType = 'res_send'; | |
| } else { | |
| // 발송시간 | |
| $smsSendDate = date('Y-m-d H:i:s', time()); | |
| $reservedAt = null; | |
| // 발송 타입 | |
| $tranType = 'send'; | |
| // SMS 발송 | |
| $cnt = $transport->send(); | |
| if ($this->publishPersonalInfoAccessLog && !self::$personalInfoAccessLogPublishFailed) { | |
| // CLI(스케줄러) 세션은 SessionManager 가 아니라 서비스 생성부터 실패할 수 있어, 이미 끝난 발송 흐름을 끊지 않도록 잡는다 | |
| try { | |
| /** @var SmsPersonalInfoAccessLogService $publishService */ | |
| $publishService = \App::getInstance(SmsPersonalInfoAccessLogService::class); | |
| // 예약발송도 등록 API 호출이 이 요청 안에서 일어나므로 발행 payload 의 accessIp 는 예약 생성 시점 관리자 IP다 | |
| $publishService->publishSmsSendAccessLog( | |
| $this->personalInfoAccessLogEventType, | |
| array_column($receivers, 'cellPhone'), | |
| $countReceivers, | |
| $senderPhoneNumber, | |
| $cnt, | |
| $reservedAt | |
| ); | |
| } catch (\Throwable $e) { | |
| self::$personalInfoAccessLogPublishFailed = true; | |
| \Logger::channel(SmsPersonalInfoAccessLogService::PRIVACY_LOG_CHANNEL)->error('[SMS 발송] 개인정보접속기록 발행 실패', [ | |
| 'exception' => $e, | |
| 'eventType' => $this->personalInfoAccessLogEventType, | |
| ]); | |
| } | |
| } | |
| // excel전송일경우 DB_SMS_EXCEL_LOG 테이블정보 삭제 | |
| if ((int) $cnt['success'] > 0 && $receiverData['receiverType'] === 'excel') { | |
변경 전SmsAuto.php
| use Framework\Utility\ArrayUtils; | |
| use Framework\Utility\ComponentUtils; | |
| use Framework\Utility\StringUtils; | |
| use Origin\Service\Member\KakaoAlrim\KakaoAlrimCloudSendService; | |
| use SplObserver; | |
| const KAKAO_ALRIM_CLOUD = 'cloud'; | |
| const KAKAO_ALRIM_BIZM = 'bizm'; | |
| const KAKAO_ALRIM_LUNA = 'luna'; // 블룸에이아이(구: 루나소프트) | |
| /** @var \Framework\Database\DBTool $db */ | |
| protected $db; | |
| /** @var \Bundle\Component\Sms\Sms $sms */ | |
| /** @var 비밀번호 확인 유무 */ | |
| protected $passwordCheckFl = true; | |
| public function __construct(array $config = []) | |
| { | |
| if (isset($config['db']) && \is_object($config['db'])) { | |
| if ($this->smsType == SmsAutoCode::MEMBER && $this->smsAutoCodeType == Code::PASS_AUTH){ | |
| $transport->setMsgType('auth'); //인증용 | |
| } | |
| $result[] = $transport->send(); | |
| } | |
| unset($receiverData); | |
| return $this->smsAutoCodeType !== Code::COUPON_MANUAL; | |
| } | |
| /** | |
| * 자동발송 내용에 치환될 데이터를 치환하는 함수 | |
| * | |
변경 후SmsAuto.php
| use Framework\Utility\ArrayUtils; | |
| use Framework\Utility\ComponentUtils; | |
| use Framework\Utility\StringUtils; | |
| use Origin\Enum\ApiClient\Notification\SmsPersonalInfoAccessLogEventType; | |
| use Origin\Service\Crm\Message\SmsPersonalInfoAccessLogService; | |
| use Origin\Service\Member\KakaoAlrim\KakaoAlrimCloudSendService; | |
| use SplObserver; | |
| const KAKAO_ALRIM_CLOUD = 'cloud'; | |
| const KAKAO_ALRIM_BIZM = 'bizm'; | |
| const KAKAO_ALRIM_LUNA = 'luna'; // 블룸에이아이(구: 루나소프트) | |
| // CLI(스케줄러) 세션은 SessionManager 가 아니라 발행 서비스 생성이 매번 실패하고 수신대상 루프 안에서 호출돼, 첫 실패 후에는 같은 프로세스에서 재시도·재로그하지 않는다 | |
| protected static $personalInfoAccessLogPublishFailed = false; | |
| /** @var \Framework\Database\DBTool $db */ | |
| protected $db; | |
| /** @var \Bundle\Component\Sms\Sms $sms */ | |
| /** @var 비밀번호 확인 유무 */ | |
| protected $passwordCheckFl = true; | |
| protected $msgType; | |
| public function __construct(array $config = []) | |
| { | |
| if (isset($config['db']) && \is_object($config['db'])) { | |
| if ($this->smsType == SmsAutoCode::MEMBER && $this->smsAutoCodeType == Code::PASS_AUTH){ | |
| $transport->setMsgType('auth'); //인증용 | |
| } | |
| $smsSendResult = $transport->send(); | |
| $smsConfig = gd_policy('sms.config'); | |
| // CLI(스케줄러) 세션은 SessionManager 가 아니라 서비스 생성부터 실패할 수 있어, 이미 끝난 발송 흐름을 끊지 않도록 잡는다 | |
| if (!self::$personalInfoAccessLogPublishFailed) { | |
| $eventType = $this->resolveAccessLogEventType(); | |
| try { | |
| /** @var SmsPersonalInfoAccessLogService $publishService */ | |
| $publishService = \App::getInstance(SmsPersonalInfoAccessLogService::class); | |
| $publishService->publishSmsSendAccessLog( | |
| $eventType, | |
| array_column($receiverData, 'cellPhone'), | |
| count($receiverData), | |
| $smsConfig['smsCallNum'] ?? '', | |
| $smsSendResult | |
| ); | |
| } catch (\Throwable $e) { | |
| self::$personalInfoAccessLogPublishFailed = true; | |
| \Logger::channel(SmsPersonalInfoAccessLogService::PRIVACY_LOG_CHANNEL)->error('[SMS 자동발송] 개인정보접속기록 발행 실패', [ | |
| 'exception' => $e, | |
| 'eventType' => $eventType, | |
| ]); | |
| } | |
| } | |
| $result[] = $smsSendResult; | |
| } | |
| unset($receiverData); | |
| return $this->smsAutoCodeType !== Code::COUPON_MANUAL; | |
| } | |
| /** | |
| * 수동 쿠폰 발급 안내만 관리자 조작 발송이라 수동 기록으로 남는다 | |
| * | |
| * @return string | |
| */ | |
| protected function resolveAccessLogEventType() | |
| { | |
| return $this->isAutoSend() | |
| ? SmsPersonalInfoAccessLogEventType::SMS_SEND_AUTO | |
| : SmsPersonalInfoAccessLogEventType::SMS_SEND_MANUAL; | |
| } | |
| /** | |
| * 자동발송 내용에 치환될 데이터를 치환하는 함수 | |
| * | |
변경 전SmsSender.php
| protected $logData; | |
| /** @var int sms 포인트 */ | |
| protected $smsPoint; | |
| /** @var int sms 전송내역 일련번호 */ | |
| protected $smsLogSno; | |
| /** @var array sms api 호출 시 사용될 정보 */ | |
| */ | |
| protected function validatePoint() | |
| { | |
| if ($this->smsPoint < $this->getSendPoint() * $this->message->count()) { | |
| $logger = \App::getInstance('logger'); | |
| $logger->info(sprintf('current sms point[%d], send count[%d], message count[%d]', $this->smsPoint, $this->getSendPoint(), $this->message->count())); | |
| $defaultReplaceCode = array_fill_keys($this->message->getReplaceCodes(), ''); | |
| $replaceCode = array_merge($defaultReplaceCode, $replaceCode); | |
| $replaceContents = $replace->appendBracesAndReplace($this->message->getContents(), $replaceCode); | |
| $hasPoint = $this->smsPoint > 1; | |
| if ($this->message->exceedSmsLength($replaceContents)) { // 치환 후 sms 길이를 초과하는 경우 lms 로 변환 | |
| $this->message = $this->message->toLmsMessage(); | |
| $replaceContents = $replace->appendBracesAndReplace($this->message->getContents(), $replaceCode); | |
| $hasPoint = $this->smsPoint > Sms::LMS_POINT; | |
| if ($this->message->exceedLmsLength($replaceContents)) { | |
| $logger->error('Message [' . $replaceContents . '] is exceed lms length'); | |
| $this->saveSendResultByFail($this->sendSms['hp'], '1002'); | |
| $this->sendSms['msg'] = $message; | |
| $this->sendSms['memberNo'] = $item['memNo']; | |
| $res = $this->callApi(); | |
| $this->smsPoint -= ($this->_isLms() ? Sms::LMS_POINT : 1); | |
| $this->callApiPostProcess($res, 1); | |
| } else { | |
| $this->saveSendResultByFail($this->sendSms['hp'], '1001'); | |
| } | |
| } | |
| $logger->info(__METHOD__ . ', end point[' . $this->smsPoint . ']'); | |
| Sms::saveSmsPoint($this->smsPoint); | |
| } | |
| /** | |
| if ($this->getReceiverGroup() > 1) { | |
| $receiverCount = count(explode(',', $this->sendSms['hp'])); | |
| } | |
| $this->smsPoint -= ($this->getSendPoint($receiverCount) * $this->message->count()); | |
| Sms::saveSmsPoint($this->smsPoint); | |
| } | |
| /** | |
| $this->callApiByLargePostProcess($res, $hpCount); | |
| // 그룹을 2개 이상 발송할 경우 sms포인트가 중복 차감 처리 되어 해당 부분 수신인으로 카운팅 처리 | |
| $this->smsPoint -= ($this->getSendPoint() * $this->message->count()); | |
| Sms::saveSmsPoint($this->smsPoint); | |
| } | |
| /** | |
| $this->smsPoint = $smsPoint; | |
| } | |
| /** | |
| * @param array $contentsMask | |
| */ | |
| return '[제목없음]'; | |
| } | |
| return $subject; | |
| } | |
| /** | |
변경 후SmsSender.php
| protected $logData; | |
| /** @var int sms 포인트 */ | |
| protected $smsPoint; | |
| protected $usePoint = true; | |
| /** @var int sms 전송내역 일련번호 */ | |
| protected $smsLogSno; | |
| /** @var array sms api 호출 시 사용될 정보 */ | |
| */ | |
| protected function validatePoint() | |
| { | |
| if ($this->usePoint === false) { | |
| return; | |
| } | |
| if ($this->smsPoint < $this->getSendPoint() * $this->message->count()) { | |
| $logger = \App::getInstance('logger'); | |
| $logger->info(sprintf('current sms point[%d], send count[%d], message count[%d]', $this->smsPoint, $this->getSendPoint(), $this->message->count())); | |
| $defaultReplaceCode = array_fill_keys($this->message->getReplaceCodes(), ''); | |
| $replaceCode = array_merge($defaultReplaceCode, $replaceCode); | |
| $replaceContents = $replace->appendBracesAndReplace($this->message->getContents(), $replaceCode); | |
| $hasPoint = $this->usePoint === false || $this->smsPoint > 1; | |
| if ($this->message->exceedSmsLength($replaceContents)) { // 치환 후 sms 길이를 초과하는 경우 lms 로 변환 | |
| $this->message = $this->message->toLmsMessage(); | |
| $replaceContents = $replace->appendBracesAndReplace($this->message->getContents(), $replaceCode); | |
| $hasPoint = $this->usePoint === false || $this->smsPoint > Sms::LMS_POINT; | |
| if ($this->message->exceedLmsLength($replaceContents)) { | |
| $logger->error('Message [' . $replaceContents . '] is exceed lms length'); | |
| $this->saveSendResultByFail($this->sendSms['hp'], '1002'); | |
| $this->sendSms['msg'] = $message; | |
| $this->sendSms['memberNo'] = $item['memNo']; | |
| $res = $this->callApi(); | |
| if ($this->usePoint) { | |
| $this->smsPoint -= ($this->_isLms() ? Sms::LMS_POINT : 1); | |
| } | |
| $this->callApiPostProcess($res, 1); | |
| } else { | |
| $this->saveSendResultByFail($this->sendSms['hp'], '1001'); | |
| } | |
| } | |
| $logger->info(__METHOD__ . ', end point[' . $this->smsPoint . ']'); | |
| if ($this->usePoint) { | |
| Sms::saveSmsPoint($this->smsPoint); | |
| } | |
| } | |
| /** | |
| if ($this->getReceiverGroup() > 1) { | |
| $receiverCount = count(explode(',', $this->sendSms['hp'])); | |
| } | |
| if ($this->usePoint) { | |
| $this->smsPoint -= ($this->getSendPoint($receiverCount) * $this->message->count()); | |
| Sms::saveSmsPoint($this->smsPoint); | |
| } | |
| } | |
| /** | |
| $this->callApiByLargePostProcess($res, $hpCount); | |
| // 그룹을 2개 이상 발송할 경우 sms포인트가 중복 차감 처리 되어 해당 부분 수신인으로 카운팅 처리 | |
| if ($this->usePoint) { | |
| $this->smsPoint -= ($this->getSendPoint() * $this->message->count()); | |
| Sms::saveSmsPoint($this->smsPoint); | |
| } | |
| } | |
| /** | |
| $this->smsPoint = $smsPoint; | |
| } | |
| /** | |
| * @param bool $usePoint | |
| */ | |
| public function setUsePoint($usePoint) | |
| { | |
| $this->usePoint = (bool)$usePoint; | |
| } | |
| /** | |
| * @param array $contentsMask | |
| */ | |
| return '[제목없음]'; | |
| } | |
| return SmsUtil::cutSubject($subject); | |
| } | |
| /** | |
변경 전LayerSmsAuthController.php
| use Component\Member\Manager; | |
| use Component\Policy\ManageSecurityPolicy; | |
| use Component\Sms\Exception\PasswordException; | |
| use Component\Sms\Sms; | |
| use Component\Sms\SmsMessage; | |
| use Component\Sms\SmsSender; | |
| use Component\Validator\Validator; | |
| use Framework\Debug\Exception\AlertReloadException; | |
| $isEmailAuth = $manager['isEmailAuth'] === 'y' && Validator::required($manager['email']); | |
| $isCsAuth = array_key_exists('csSno', $manager) && $manager['csSno'] > 0; | |
| $isInvalidPassword = false; | |
| $smsPoint = Sms::getPoint(true); | |
| $hasSmsPoint = ($smsPoint >= 1); | |
| $email = $cellPhone = ''; | |
| $securitySelect = \App::load(ManageSecurityPolicy::class)->getLoginSecuritySelect(); | |
| if (array_key_exists('smsReSend', $securitySelect) && !$hasSmsPoint) { | |
| try { | |
| $sender = \App::load(SmsSender::class); | |
| $sender->setIsThrowPasswordException(true); | |
| $sender->setMessage(new SmsMessage('SMS 인증번호 검증')); | |
| $receiver = [ | |
| [ | |
| 'memNo' => '0', | |
| 'cellPhone' => '00000000000', | |
| ], | |
| ]; | |
| $sender->setReceiver($receiver); | |
| $sender->setSmsType('guest'); | |
| $sender->setLogData(['disableResend' => true]); | |
| $sender->validPassword(\App::load(\Component\Sms\SmsUtil::class)->getPassword()); | |
| $sender->setSmsPoint(1); | |
| $sender->send(); | |
| } catch (PasswordException $e) { | |
| if (array_key_exists('emailSend', $securitySelect)) { | |
| unset($securitySelect['smsReSend']); | |
| } else { | |
| $isInvalidPassword = true; | |
| } | |
| } | |
| } | |
| $countSelect = \count($securitySelect); | |
| if ($isCsAuth) { | |
| $emptyAuthMethod = true; | |
| $securitySelect = ['emailSend' => '이메일']; | |
| } elseif ($countSelect === 2) { | |
| if ($isSmsAuth && !$isEmailAuth && !$hasSmsPoint) { | |
| unset($securitySelect['smsReSend']); | |
| $emptySmsPoint = true; | |
| } elseif (!$isSmsAuth && !$isEmailAuth) { | |
| $securitySelect = []; | |
| } elseif ($isSmsAuth && !$isEmailAuth) { | |
| unset($securitySelect['emailSend']); | |
| } elseif ($isEmailAuth && (!$isSmsAuth || !$hasSmsPoint)) { | |
| unset($securitySelect['smsReSend']); | |
| } | |
| } elseif (array_key_exists('smsReSend', $securitySelect) && $countSelect === 1) { | |
| if (!$hasSmsPoint) { | |
| $emptySmsPoint = true; | |
| } elseif (!$isSmsAuth && $hasSmsPoint) { | |
| unset($securitySelect['smsReSend']); | |
| } | |
| } elseif (array_key_exists('emailSend', $securitySelect) && $countSelect === 1 && !$isEmailAuth) { | |
| unset($securitySelect['emailSend']); | |
| } | |
| if ($isSmsAuth && $hasSmsPoint) { | |
| $phoneArr = explode('-', $manager['cellPhone']); | |
| $phoneLen = \strlen($phoneArr[1]); | |
| $s = ''; | |
변경 후LayerSmsAuthController.php
| use Component\Member\Manager; | |
| use Component\Policy\ManageSecurityPolicy; | |
| use Component\Validator\Validator; | |
| use Framework\Debug\Exception\AlertReloadException; | |
| $isEmailAuth = $manager['isEmailAuth'] === 'y' && Validator::required($manager['email']); | |
| $isCsAuth = array_key_exists('csSno', $manager) && $manager['csSno'] > 0; | |
| $isInvalidPassword = false; | |
| $email = $cellPhone = ''; | |
| $securitySelect = \App::load(ManageSecurityPolicy::class)->getLoginSecuritySelect(); | |
| $countSelect = \count($securitySelect); | |
| if ($isCsAuth) { | |
| $emptyAuthMethod = true; | |
| $securitySelect = ['emailSend' => '이메일']; | |
| } elseif ($countSelect === 2) { | |
| if (!$isSmsAuth && !$isEmailAuth) { | |
| $securitySelect = []; | |
| } elseif ($isSmsAuth && !$isEmailAuth) { | |
| unset($securitySelect['emailSend']); | |
| } elseif ($isEmailAuth && !$isSmsAuth) { | |
| unset($securitySelect['smsReSend']); | |
| } | |
| } elseif (array_key_exists('smsReSend', $securitySelect) && $countSelect === 1) { | |
| if (!$isSmsAuth) { | |
| unset($securitySelect['smsReSend']); | |
| } | |
| } elseif (array_key_exists('emailSend', $securitySelect) && $countSelect === 1 && !$isEmailAuth) { | |
| unset($securitySelect['emailSend']); | |
| } | |
| if ($isSmsAuth) { | |
| $phoneArr = explode('-', $manager['cellPhone']); | |
| $phoneLen = \strlen($phoneArr[1]); | |
| $s = ''; | |
변경 전LoginPsController.php
| use Component\Member\Manager; | |
| use Component\Member\ManagerCs; | |
| use Component\Policy\ManageSecurityPolicy; | |
| use Component\Sms\Exception\PasswordException; | |
| use Component\Sms\SmsSender; | |
| use Component\Validator\Validator; | |
| use Exception; | |
| use Framework\Debug\Exception\AlertRedirectException; | |
| // 임시 세션 정보 | |
| $tmpManager = $session->get(Manager::SESSION_TEMP_MANAGER); | |
| $manager = \App::load(Manager::class); | |
| $smsSender = \App::load(SmsSender::class); | |
| $smsSender->setIsThrowPasswordException(true); | |
| $smsAuth = $manager->sendSmsAuthNumber($tmpManager); | |
| $tmpManager['smsAuthNumber'] = $smsAuth['smsAuthNumber']; | |
| $tmpManager['isSmsLogin'] = $smsAuth['message']; | |
| throw $e; | |
| } catch (LayerNotReloadException $e) { | |
| throw $e; | |
| } catch (PasswordException $e) { | |
| $this->json( | |
| [ | |
| 'error' => 5, | |
| 'message' => $e->getMessage(), | |
| ] | |
| ); | |
| } catch (Exception $e) { | |
| $adminLog->setAdminLog(); | |
| $logger->warning($e->getMessage() . ', ' . $e->getFile() . ', ' . $e->getLine(), $e->getTrace()); | |
변경 후LoginPsController.php
| use Component\Member\Manager; | |
| use Component\Member\ManagerCs; | |
| use Component\Policy\ManageSecurityPolicy; | |
| use Component\Validator\Validator; | |
| use Exception; | |
| use Framework\Debug\Exception\AlertRedirectException; | |
| // 임시 세션 정보 | |
| $tmpManager = $session->get(Manager::SESSION_TEMP_MANAGER); | |
| $manager = \App::load(Manager::class); | |
| $smsAuth = $manager->sendSmsAuthNumber($tmpManager); | |
| $tmpManager['smsAuthNumber'] = $smsAuth['smsAuthNumber']; | |
| $tmpManager['isSmsLogin'] = $smsAuth['message']; | |
| throw $e; | |
| } catch (LayerNotReloadException $e) { | |
| throw $e; | |
| } catch (Exception $e) { | |
| $adminLog->setAdminLog(); | |
| $logger->warning($e->getMessage() . ', ' . $e->getFile() . ', ' . $e->getLine(), $e->getTrace()); | |
변경 전SmsAutoController.php
| use Component\Sms\Sms; | |
| use Framework\Utility\ComponentUtils; | |
| use Framework\Utility\ConfigUrlUtils; | |
| /** | |
| * SMS 자동 발송 설정 | |
| $request = \App::getInstance('request'); | |
| // --- 메뉴 설정 | |
| $this->callMenu('member', 'sms', 'auto'); | |
| $smsAdmin = \App::load('Component\\Sms\\SmsAdmin'); | |
| $smsAutoData = $smsAdmin->getSmsAutoData(); | |
| // SMS 발신번호 사전 등록 번호 정보 | |
| $godoSms = \App::load('Component\\Godo\\GodoSmsServerApi'); | |
| $smsPreRegister = $godoSms->checkSmsCallNumber($smsAutoData['smsCallNum']); | |
| $checked = $smsAutoData['checked']; | |
| $this->setData('smsAutoReservationTime', $smsAdmin->getSmsReservationTime()); | |
| $this->setData('smsAutoData', gd_htmlspecialchars($smsAutoData)); | |
| $this->setData('checked', gd_isset($checked)); | |
| $this->setData('smsPreRegister', $smsPreRegister); | |
| $this->setData('policy', ComponentUtils::getPolicy('sms.sms080')); | |
| $this->setData('memberJoin', ComponentUtils::getPolicy('member.join')); | |
| $this->setData('useApprovalFlag', ($memberJoin['appUseFl'] != 'n' || $memberJoin['under14Fl'] == 'y')); | |
| $gLicense = $globals->get('gLicense'); | |
| $this->setData('mallSettingDate', $gLicense['sdate']); | |
| $this->setData('commerceSmsIntroUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('sms-intro')); | |
| $this->setData('commerceShopMainUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('shop-main')); | |
| $this->setData('commerceSmsRefusalIntroUrl', ConfigUrlUtils::getNhnCommerceEchostUrl('sms-refusal-intro')); | |
| } | |
변경 후SmsAutoController.php
| use Component\Sms\Sms; | |
| use Framework\Utility\ComponentUtils; | |
| use Framework\Utility\ConfigUrlUtils; | |
| use Origin\Service\Crm\Message\SmsManageCallNumberService; | |
| /** | |
| * SMS 자동 발송 설정 | |
| $request = \App::getInstance('request'); | |
| // --- 메뉴 설정 | |
| $this->callMenu('member', 'sms', 'auto'); | |
| // 인터셉터의 menuCode 보완은 postHandle 이라 index() 안의 admin API 호출보다 늦다 | |
| $this->setMenuCode('member', 'sms', 'auto'); | |
| $smsAdmin = \App::load('Component\\Sms\\SmsAdmin'); | |
| $smsAutoData = $smsAdmin->getSmsAutoData(); | |
| /** @var SmsManageCallNumberService $smsManageCallNumberService */ | |
| $smsManageCallNumberService = \App::getInstance(SmsManageCallNumberService::class); | |
| $hasRegisteredCallNumber = $smsManageCallNumberService->hasRegisteredCallNumber(); | |
| $checked = $smsAutoData['checked']; | |
| $this->setData('smsAutoReservationTime', $smsAdmin->getSmsReservationTime()); | |
| $this->setData('smsAutoData', gd_htmlspecialchars($smsAutoData)); | |
| $this->setData('checked', gd_isset($checked)); | |
| $this->setData('hasRegisteredCallNumber', $hasRegisteredCallNumber); | |
| $this->setData('policy', ComponentUtils::getPolicy('sms.sms080')); | |
| $this->setData('memberJoin', ComponentUtils::getPolicy('member.join')); | |
| $this->setData('useApprovalFlag', ($memberJoin['appUseFl'] != 'n' || $memberJoin['under14Fl'] == 'y')); | |
| $gLicense = $globals->get('gLicense'); | |
| $this->setData('mallSettingDate', $gLicense['sdate']); | |
| $this->setData('commerceShopMainUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('shop-main')); | |
| $this->setData('commerceSmsRefusalIntroUrl', ConfigUrlUtils::getNhnCommerceEchostUrl('sms-refusal-intro')); | |
| } | |
변경 전SmsPsController.php
| <?php | |
| /** | |
| * This is commercial software, only users who have purchased a valid license | |
| * and accept to the terms of the License Agreement can install and use this | |
| * program. | |
| * | |
| * Do not edit or add to this file if you wish to upgrade Godomall5 to newer | |
| * versions in the future. | |
| * | |
| * @copyright ⓒ 2016, NHN godo: Corp. | |
| * @link http://www.godo.co.kr | |
| */ | |
| namespace Bundle\Controller\Admin\Member; | |
| use Component\Godo\GodoSmsServerApi; | |
| use Component\Sms\Exception\PasswordException; | |
| use Component\Sms\SmsAdmin; | |
| use Framework\Debug\Exception\LayerException; | |
| use Framework\Debug\Exception\LayerNotReloadException; | |
| use Origin\Service\Admin\Member\ManagerTutorial\HelperPopupExposureAvailabilityService; | |
| use Origin\Enum\ManagerTutorialType; | |
| /** | |
| * SMS 관련 처리 | |
| * | |
| * @author Shin Donggyu <artherot@godo.co.kr> | |
| */ | |
| class SmsPsController extends \Controller\Admin\Controller | |
| { | |
| /** | |
| * index | |
| * | |
| * @throws LayerException | |
| */ | |
| public function index() | |
| { | |
| // --- 모듈 호출 | |
| $request = \App::getInstance('request'); | |
| $logger = \App::getInstance('logger'); | |
| $postData = $request->post()->toArray(); | |
| $mode = $request->post()->get('mode'); | |
| switch (gd_isset($mode)) { | |
| case 'smsAuto': // SMS 자동발송 설정 | |
| try { | |
| $smsAdmin = \App::load(SmsAdmin::class); | |
| $smsAdmin->saveSmsAuto($postData); | |
| // 운영 도우미 팝업 노출 | |
| $helperPopupExposureAvailabilityService = \App::getInstance(HelperPopupExposureAvailabilityService::class); | |
| $getTutorialUrl = $helperPopupExposureAvailabilityService->getTutorialUrl(ManagerTutorialType::SMS_REGISTER); | |
| if (!empty($getTutorialUrl)) { | |
| $this->layer(__('저장이 완료되었습니다.'), 'parent.tutorial_layer("' . $getTutorialUrl . '");'); | |
| } | |
| $this->layer(__('저장이 완료되었습니다.')); | |
| } catch (\Exception $e) { | |
| $item = ($e->getMessage() ? ' - ' . str_replace("\n", ' - ', $e->getMessage()) : ''); | |
| throw new LayerException(__('처리중에 오류가 발생하여 실패되었습니다.') . $item); | |
| } | |
| break; | |
| case 'smsCallNumSave': // SMS 발신번호 저장 | |
| try { | |
| $smsAdmin = \App::load(SmsAdmin::class); | |
| $result = $smsAdmin->saveCallNum($postData['smsCallNum']); | |
| if ($result === true) { | |
| echo 'OK'; | |
| } else { | |
| echo 'FAIL'; | |
| } | |
| } catch (\Exception $e) { | |
| echo 'FAIL'; | |
| } | |
| break; | |
| case 'contentsRegister': // SMS 내용 등록 | |
| case 'contentsModify': // SMS 내용 수정 | |
| try { | |
| $smsAdmin = \App::load(SmsAdmin::class); | |
| $smsAdmin->saveContentsData($postData); | |
| $this->layer(__('저장이 완료되었습니다.')); | |
| } catch (\Exception $e) { | |
| $item = ($e->getMessage() ? ' - ' . str_replace("\n", ' - ', $e->getMessage()) : ''); | |
| throw new LayerException(__('처리중에 오류가 발생하여 실패되었습니다.') . $item); | |
| } | |
| break; | |
| case 'contentsDelete': // SMS 내용 수정 | |
| try { | |
| $smsAdmin = \App::load(SmsAdmin::class); | |
| $smsAdmin->deleteContentsData($request->post()->get('sno')); | |
| $this->layer(__('저장이 완료되었습니다.')); | |
| } catch (\Exception $e) { | |
| $item = ($e->getMessage() ? ' - ' . str_replace("\n", ' - ', $e->getMessage()) : ''); | |
| throw new LayerException(__('처리중에 오류가 발생하여 실패되었습니다.') . $item); | |
| } | |
| break; | |
| case 'smsSend': // 발송 | |
| try { | |
| $smsAdmin = \App::load(SmsAdmin::class); | |
| $cnt = $smsAdmin->sendSms($postData); | |
| $info = ' (' . $cnt['success'] . __('건 성공') . ' / ' . $cnt['fail'] . __('건 실패') . ')'; | |
| $logger->info($info, $postData); | |
| if ((int) $cnt['success'] > 0) { | |
| echo "<script> parent.BootstrapDialog.closeAll(); </script>"; | |
| $this->layer(__('SMS 발송 요청이 완료되었습니다. 자세한 내용은 SMS 발송 내역 보기 확인 바랍니다.'), 'parent.location.href="' . $request->getReferer() . '"'); | |
| } else { | |
| throw new \Exception('SEND_FAIL'); | |
| } | |
| } catch (PasswordException $e) { | |
| echo "<script> parent.BootstrapDialog.closeAll(); </script>"; | |
| throw new LayerNotReloadException($e->getMessage(), null, null, $e->getScript(), 4000); | |
| } catch (\Exception $e) { | |
| echo "<script> parent.BootstrapDialog.closeAll(); </script>"; | |
| if ($e->getMessage() == 'SEND_FAIL') { | |
| throw new LayerNotReloadException(__('SMS 발송 요청이 실패했습니다.'), null, null, null, 4000); | |
| } else { | |
| $item = ($e->getMessage() ? ' - ' . str_replace("\n", ' - ', $e->getMessage()) : ''); | |
| throw new LayerNotReloadException(__('처리중에 오류가 발생하여 실패되었습니다.') . $item, null, null, null, 4000); | |
| } | |
| } | |
| break; | |
| case 'cancel_all_member'://예약문자 전체회원 취소 | |
| try { | |
| // --- SMS 모듈 | |
| $godoSms = new GodoSmsServerApi(); | |
| $componentSmsLog = \App::load('Component\\Sms\\SmsLog'); | |
| $smsLog = $componentSmsLog->getSmsLog('*', $postData['smsLogSno']); | |
| if ($smsLog['replaceCodeType'] == 'none') { | |
| // SMS 결과 수신 처리 | |
| $godoSms->smsReserveChange($postData['smsLogSno'], 'cancelAll', [], true); | |
| $js = 'parent.hide_process();'; | |
| $js .= 'top.dialog_alert(\'전체 예약취소가 완료되었습니다.\', \'SMS 발송 내역 상세보기\', {callback: function(){parent.layer_list_search(\''.$postData['page'].'\')}});'; | |
| $this->js($js); | |
| } else { | |
| // SMS 결과 수신 처리 | |
| if ($godoSms->smsSendListReserveChange($postData['smsLogSno'], 'cancelAll', [], true)) { | |
| //SMS 발송 내역 상세보기 | |
| $js = 'parent.hide_process();'; | |
| $js .= 'top.dialog_alert(\'전체 예약취소가 완료되었습니다.\', \'SMS 발송 내역 상세보기\', {callback: function(){parent.layer_list_search(\''.$postData['page'].'\')}});'; | |
| $this->js($js); | |
| } else { | |
| throw new LayerException('통신이 정상적이지 않습니다.\r\n잠시 후 다시 시도해 주세요.', null, null, null, 3000); | |
| } | |
| } | |
| } catch (\Exception $e) { | |
| throw new LayerException($e->getMessage(), null, null, null, 3000); | |
| } | |
| break; | |
| case 'cancel_select_member'://예약문자 특정회원 취소 | |
| case 'cancel_search_member'://예약문자 검색회원 취소 | |
| try { | |
| // --- SMS 모듈 | |
| $godoSms = new GodoSmsServerApi(); | |
| $componentSmsLog = \App::load('Component\\Sms\\SmsLog'); | |
| $smsLog = $componentSmsLog->getSmsLog('*', $postData['smsLogSno']); | |
| $smeModeText = '선택'; | |
| if($mode == 'cancel_search_member'){ | |
| if($postData['smsKey'] == 'receiverCellPhone') { | |
| $postData['smsKeyword'] = str_replace('-', '', $postData['smsKeyword']); | |
| } | |
| $postData['noPaging'] = 'y'; | |
| unset($postData['sno']); | |
| $getData = $componentSmsLog->getSmsSendList($postData); | |
| $postData['sno'] = array(); | |
| foreach($getData['data'] as $smsSendItem){ | |
| if($smsSendItem['sendCheckFl'] != 'c'){ | |
| $postData['sno'][] = $smsSendItem['sno']; | |
| } | |
| } | |
| $smeModeText = '검색'; | |
| } | |
| if ($smsLog['replaceCodeType'] == 'none') { | |
| // SMS 결과 수신 처리 | |
| $godoSms->smsReserveChange($postData['smsLogSno'], 'cancel', $postData['sno'], true); | |
| $js = 'parent.hide_process();'; | |
| $js .= 'top.dialog_alert(\''.$smeModeText.'회원 예약취소가 완료되었습니다.\', \'SMS 발송 내역 상세보기\', {callback: function(){parent.layer_list_search(\''.$postData['page'].'\')}});'; | |
| $this->js($js); | |
| } else { | |
| // SMS 결과 수신 처리 | |
| if ($godoSms->smsSendListReserveChange($postData['smsLogSno'], 'cancel', $postData['sno'], true)) { | |
| $js = 'parent.hide_process();'; | |
| $js .= 'top.dialog_alert(\''.$smeModeText.'회원 예약취소가 완료되었습니다.\', \'SMS 발송 내역 상세보기\', {callback: function(){parent.layer_list_search(\''.$postData['page'].'\')}});'; | |
| $this->js($js); | |
| } else { | |
| echo "<script> parent.hide_process(); </script>"; | |
| throw new LayerException('통신이 정상적이지 않습니다.\r\n잠시 후 다시 시도해 주세요.', null, null, null, 3000); | |
| } | |
| } | |
| } catch (LayerException $e) { | |
| throw new LayerException($e->getMessage(), null, null, null, 3000); | |
| } | |
| break; | |
| } | |
| exit(); | |
| } | |
| } | |
변경 후SmsPsController.php
변경 전SmsSendController.php
| namespace Bundle\Controller\Admin\Member; | |
| use Component\Code\Code; | |
| use Component\Godo\GodoSmsServerApi; | |
| use Component\Member\Group\Util; | |
| use Component\Member\MemberDAO; | |
| use Component\Sms\Sms; | |
| use Framework\Utility\DateTimeUtils; | |
| use Framework\Utility\GodoUtils; | |
| use Framework\Utility\StringUtils; | |
| use Origin\Service\Member\CrmGroupService; | |
| /** | |
| * SMS 개별/전체 발송 | |
| */ | |
| if (gd_is_provider()) { // 공급사관리 | |
| $this->callMenu('order', 'order', 'smsSend'); | |
| } else { | |
| $this->callMenu('member', 'sms', 'send'); | |
| } | |
| $logger = \App::getInstance('logger'); | |
| $request = \App::getInstance('request'); | |
| $popupMode = false; | |
| $receiverMode = 'select'; | |
| // SMS 발신번호 사전 등록 번호 정보 | |
| $smsCallNum = $smsAdmin->getSmsCallNum(); | |
| $godoSms = new GodoSmsServerApi(); | |
| $smsPreRegister = $godoSms->checkSmsCallNumber($smsCallNum); | |
| $memberGroupNames = Util::getGroupName(); | |
| if ($request->get()->has('opener')) { | |
| $this->setData('smsForbidTime', Sms::SMS_FORBID_TIME); | |
| $this->setData('lmsPoint', Sms::LMS_POINT); | |
| $this->setData('smsCallNum', $smsCallNum); | |
| $this->setData('smsPreRegister', $smsPreRegister); | |
| if ($this->getData('receiverCount') < 1) { | |
| $this->setData('receiverCount', count($receiverData)); | |
| } | |
| ); | |
| $this->setData('excelUploadExposing', $excelUploadExposing); | |
| } | |
| $this->setData('commerceSmsIntroUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('sms-intro')); | |
| $this->setData('commerceShopMainUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('shop-main')); | |
| } | |
변경 후SmsSendController.php
| namespace Bundle\Controller\Admin\Member; | |
| use Component\Code\Code; | |
| use Component\Member\Group\Util; | |
| use Component\Member\MemberDAO; | |
| use Component\Sms\Sms; | |
| use Framework\Utility\DateTimeUtils; | |
| use Framework\Utility\GodoUtils; | |
| use Framework\Utility\StringUtils; | |
| use Origin\Service\Crm\Message\SmsManageCallNumberService; | |
| /** | |
| * SMS 개별/전체 발송 | |
| */ | |
| if (gd_is_provider()) { // 공급사관리 | |
| $this->callMenu('order', 'order', 'smsSend'); | |
| // 인터셉터의 menuCode 보완은 postHandle 이라 index() 안의 admin API 호출보다 늦다 | |
| $this->setMenuCode('order', 'order', 'smsSend'); | |
| } else { | |
| $this->callMenu('member', 'sms', 'send'); | |
| $this->setMenuCode('member', 'sms', 'send'); | |
| } | |
| $logger = \App::getInstance('logger'); | |
| $request = \App::getInstance('request'); | |
| $popupMode = false; | |
| $receiverMode = 'select'; | |
| /** @var SmsManageCallNumberService $smsManageCallNumberService */ | |
| $smsManageCallNumberService = \App::getInstance(SmsManageCallNumberService::class); | |
| $smsCallNum = $smsManageCallNumberService->getCallNumber(); | |
| $hasRegisteredCallNumber = $smsManageCallNumberService->hasRegisteredCallNumber(); | |
| $memberGroupNames = Util::getGroupName(); | |
| if ($request->get()->has('opener')) { | |
| $this->setData('smsForbidTime', Sms::SMS_FORBID_TIME); | |
| $this->setData('lmsPoint', Sms::LMS_POINT); | |
| $this->setData('smsCallNum', $smsCallNum); | |
| $this->setData('hasRegisteredCallNumber', $hasRegisteredCallNumber); | |
| if ($this->getData('receiverCount') < 1) { | |
| $this->setData('receiverCount', count($receiverData)); | |
| } | |
| ); | |
| $this->setData('excelUploadExposing', $excelUploadExposing); | |
| } | |
| $this->setData('commerceShopMainUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('shop-main')); | |
| } | |
변경 전AdminLogListController.php
| namespace Bundle\Controller\Admin\Policy; | |
| use Bundle\Component\Member\Member; | |
| use Component\Admin\AdminLogDAO; | |
| class AdminLogListController extends \Controller\Admin\Controller | |
| { | |
| public function index() | |
| { | |
| $this->callMenu('policy', 'management', 'adminLogList'); | |
| $adminLog = new AdminLogDAO(); | |
| $request = \Request::get()->toArray(); | |
| gd_isset($request['view'], 'default'); | |
| $request['searchDate'][0] = $request['searchDate'][0] ? $request['searchDate'][0] : date('Y-m-d', strtotime('-6 day')); | |
| $request['searchDate'][1] = $request['searchDate'][1] ? $request['searchDate'][1] : date('Y-m-d'); | |
| // 페이지 구분 | |
| $request['adminAccessPage'] = 'adminInfo'; | |
| if ($request['view'] == 'adminAccess') { | |
| $request['adminAccessFl'] = true; | |
| } | |
| $fields = ['sno','regDt', 'ip','managerId','menu','page','action','baseUri','data']; | |
| $data = $adminLog->getList($request,$fields,\Request::get()->get('pageNum',20)); | |
| // 정규식 패턴 view 파라미터 제거 | |
| $pattern = '/view=[^&]+$|view=[^&]+&/'; | |
| $this->setData('data',$data); | |
| $this->setData('view', $request['view']); | |
| $this->setData('queryString', $queryString); | |
| $this->setData('searchKindASelectBox', Member::getSearchKindASelectBox()); | |
| } | |
| } | |
변경 후AdminLogListController.php
| namespace Bundle\Controller\Admin\Policy; | |
| use Component\Admin\AdminLogDAO; | |
| use Origin\Service\Crm\Message\SmsPersonalInfoAccessLogListDataService; | |
| class AdminLogListController extends \Controller\Admin\Controller | |
| { | |
| const VIEW_DEFAULT = 'default'; | |
| const VIEW_ADMIN_LIST = 'adminList'; | |
| const VIEW_ADMIN_ACCESS = 'adminAccess'; | |
| const VIEW_SMS_LIST = 'smsList'; | |
| const SEARCH_KIND_EQUAL_SEARCH = 'equalSearch'; | |
| public function index() | |
| { | |
| $this->callMenu('policy', 'management', 'adminLogList'); | |
| // notification-admin 은 공통 헤더의 menuCode 로 관리자 인증을 하므로 API 조회 전에 채워둔다 | |
| $this->setMenuCode('policy', 'management', 'adminLogList'); | |
| $request = \Request::get()->toArray(); | |
| gd_isset($request['view'], self::VIEW_DEFAULT); | |
| // 개인정보 과다 조회를 막기 위해 이 화면은 부분포함 검색을 제공하지 않는다 | |
| $request['searchKind'] = self::SEARCH_KIND_EQUAL_SEARCH; | |
| $request['searchDate'][0] = $request['searchDate'][0] ? $request['searchDate'][0] : date('Y-m-d', strtotime('-6 day')); | |
| $request['searchDate'][1] = $request['searchDate'][1] ? $request['searchDate'][1] : date('Y-m-d'); | |
| $pageNum = \Request::get()->get('pageNum', 20); | |
| if ($request['view'] == self::VIEW_SMS_LIST) { | |
| $data = $this->getSmsPersonalInfoAccessLogData($request, (int)$pageNum); | |
| } else { | |
| $adminLog = new AdminLogDAO(); | |
| // 페이지 구분 | |
| $request['adminAccessPage'] = 'adminInfo'; | |
| if ($request['view'] == self::VIEW_ADMIN_ACCESS) { | |
| $request['adminAccessFl'] = true; | |
| } | |
| $fields = ['sno', 'regDt', 'ip', 'managerId', 'menu', 'page', 'action', 'baseUri', 'data']; | |
| $data = $adminLog->getList($request, $fields, $pageNum); | |
| } | |
| // 정규식 패턴 view 파라미터 제거 | |
| $pattern = '/view=[^&]+$|view=[^&]+&/'; | |
| $this->setData('data',$data); | |
| $this->setData('view', $request['view']); | |
| $this->setData('queryString', $queryString); | |
| $this->setData('displayMenuColumnFl', $request['view'] == self::VIEW_DEFAULT); | |
| $this->setData('displayPageColumnFl', in_array($request['view'], [self::VIEW_DEFAULT, self::VIEW_ADMIN_LIST, self::VIEW_SMS_LIST], true)); | |
| $this->setData('displayPersonalInfoNoteFl', $request['view'] != self::VIEW_SMS_LIST); | |
| } | |
| /** | |
| * SMS 관련 개인정보 접속기록 목록 조회 | |
| * | |
| * @param array $request | |
| * @param int $pageNum | |
| * @return array list, count, page, request, apiErrorFl | |
| */ | |
| protected function getSmsPersonalInfoAccessLogData(array $request, int $pageNum): array | |
| { | |
| /** @var SmsPersonalInfoAccessLogListDataService $smsPersonalInfoAccessLogListDataService */ | |
| $smsPersonalInfoAccessLogListDataService = \App::getInstance(SmsPersonalInfoAccessLogListDataService::class); | |
| return $smsPersonalInfoAccessLogListDataService->getSmsPersonalInfoAccessLogData($request, $pageNum); | |
| } | |
| } | |
변경 전ManageListController.php
| use Component\Member\ManagerCs; | |
| use Component\Page\Page; | |
| use Component\Scm\Scm; | |
| use Framework\Debug\Exception\LayerException; | |
| use Framework\Utility\GodoUtils; | |
| use Framework\Utility\StringUtils; | |
| $component = \App::load(Manager::class); | |
| $getValue = $request->get()->toArray(); | |
| $getData = $component->getManagerList($getValue); | |
| $page = \App::load(Page::class); // 페이지 재설정 | |
| $department = gd_code('02001'); // 부서 | |
| $position = gd_code('02002'); // 직급 | |
| $smsAutoReceiveKind = $component->smsAutoReceiveKind; | |
| $smsAutoReceiveKind = array_merge(['all' => __('전체')], ['n' => __('SMS 수신안함')], $smsAutoReceiveKind); | |
| // 장기 미로그인 운영자 안내 | |
| $dataSecurity = gd_policy('manage.security'); | |
| gd_isset($dataSecurity['noVisitAlarmFl'], 'n'); | |
| $this->setData('position', $position); | |
| $this->setData('duty', $duty); | |
| $this->setData('smsAutoReceiveKind', $smsAutoReceiveKind); | |
| $this->setData('scmList', $scmList); | |
| $this->addCss(['layer.css']); | |
| } | |
변경 후ManageListController.php
| use Component\Member\ManagerCs; | |
| use Component\Page\Page; | |
| use Component\Scm\Scm; | |
| use Origin\Enum\Crm\Message\MessageOperatorRoleType; | |
| use Origin\Service\Crm\Message\MessageOperatorRoleService; | |
| use Framework\Debug\Exception\LayerException; | |
| use Framework\Utility\GodoUtils; | |
| use Framework\Utility\StringUtils; | |
| $component = \App::load(Manager::class); | |
| $getValue = $request->get()->toArray(); | |
| $getData = $component->getManagerList($getValue); | |
| // SMS 권한 컬럼 라벨 (조회된 행 데이터만으로 판정 — 추가 쿼리 없음) | |
| /** @var MessageOperatorRoleService $messageOperatorRoleService */ | |
| $messageOperatorRoleService = \App::getInstance(MessageOperatorRoleService::class); | |
| if (is_array($getData['data'])) { | |
| foreach ($getData['data'] as $messageRoleRowKey => $messageRoleRow) { | |
| $getData['data'][$messageRoleRowKey]['smsRoleName'] = MessageOperatorRoleType::getName($messageOperatorRoleService->extractRole($messageRoleRow)); | |
| } | |
| } | |
| $page = \App::load(Page::class); // 페이지 재설정 | |
| $department = gd_code('02001'); // 부서 | |
| $position = gd_code('02002'); // 직급 | |
| $smsAutoReceiveKind = $component->smsAutoReceiveKind; | |
| $smsAutoReceiveKind = array_merge(['all' => __('전체')], ['n' => __('SMS 수신안함')], $smsAutoReceiveKind); | |
| // SMS 권한 검색 라디오 목록 ('all'=전체 는 필터 미적용 UI 옵션이라 역할 목록 앞에 붙임) | |
| $smsRoleList = ['all' => __('전체')] + MessageOperatorRoleType::availableForSearch(); | |
| // 장기 미로그인 운영자 안내 | |
| $dataSecurity = gd_policy('manage.security'); | |
| gd_isset($dataSecurity['noVisitAlarmFl'], 'n'); | |
| $this->setData('position', $position); | |
| $this->setData('duty', $duty); | |
| $this->setData('smsAutoReceiveKind', $smsAutoReceiveKind); | |
| $this->setData('smsRoleList', $smsRoleList); | |
| $this->setData('scmList', $scmList); | |
| $this->addCss(['layer.css']); | |
| } | |
변경 전ManagePermissionController.php
| namespace Bundle\Controller\Admin\Policy; | |
| //use Globals; | |
| //use Request; | |
| //use Session; | |
| $this->addScript([ | |
| 'managePermission.js?'.time(), // 운영자 권한 설정 JS | |
| ]); | |
| } | |
| public function post() | |
변경 후ManagePermissionController.php
| namespace Bundle\Controller\Admin\Policy; | |
| use Origin\Enum\Crm\Message\MessageOperatorRoleType; | |
| //use Globals; | |
| //use Request; | |
| //use Session; | |
| $this->addScript([ | |
| 'managePermission.js?'.time(), // 운영자 권한 설정 JS | |
| ]); | |
| // SMS 권한 검색 필터 목록 ('all'=전체 + 역할) | |
| $this->setData('smsRoleList', ['all' => __('전체')] + MessageOperatorRoleType::availableForSearch()); | |
| } | |
| public function post() | |
변경 전ManagePsController.php
| use Component\Policy\ManageSecurityPolicy; | |
| use Component\Policy\Policy; | |
| use Component\Scm\ScmAdmin; | |
| use Component\Sms\Sms; | |
| use Component\Sms\SmsMessage; | |
| use Component\Sms\SmsSender; | |
| use Exception; | |
| use Framework\Debug\Exception\LayerNotReloadException; | |
| use Framework\Security\Otp; | |
| use Framework\Utility\ComponentUtils; | |
| use Framework\Utility\StringUtils; | |
| use Framework\Security\Token; | |
| /** | |
| * 운영자 등록/수정 처리 페이지 | |
| $request = \App::getInstance('request'); | |
| $session = \App::getInstance('session'); | |
| $_managerClass = \App::load(Manager::class); | |
| // 각 모드에 따른 처리 | |
| switch ($request->request()->get('mode')) { | |
| case 'modify': // --- 관리자 등록 / 수정 | |
| if (gd_is_provider()) { | |
| $managerData = $_managerClass->getManagerData($request->post()->get('sno')); | |
| $this->layer(__('잘못된 경로로 접근하셨습니다.'), 'parent.location.reload()'); | |
| } | |
| case 'register': | |
| try { | |
| if ((DEFAULT_CODE_SCMNO === (int)$request->post()->get('sno')) && (DEFAULT_CODE_SCMNO !== (int)$session->get('manager.sno'))) { | |
| $this->layer(__('잘못된 경로로 접근하셨습니다.'), 'parent.location.reload()'); | |
| } | |
| // 데모인 경우 패스워드 수정 금지 | |
| $demo = \App::load(GodoDemoApi::class); | |
| if ($demo->checkDomain() === true && $request->request()->get('mode') === 'modify') { | |
| $request->post()->del('isModManagerPw'); | |
| } | |
| if ($request->post()->get('permissionSetupMode') == 'managePermission' && $this->getData('managerPermissionMethodExist') === true && $this->getData('adminMenuPermissionMethodExist') === true) { | |
| $postData = $request->post()->toArray(); | |
| if (method_exists($_managerClass, 'setManagerWorkDebugPermissionFl')) { | |
| $_managerClass->saveManagerWritePermissionData($postData, $managerSno); | |
| $_managerClass->saveManagerIp($postData,$managerSno); | |
| } else { | |
| $_managerClass->saveManagerData($request->post()->toArray(), $request->files()->toArray()); | |
| } | |
| $this->layer(__('저장이 완료되었습니다.') . '<br/><b class="notice-danger notice-info">'.__('개인정보보호법에 따라 개인정보처리시스템에 접속하는') . '<br>'.__('개인정보취급자(운영자)인 경우에는 접속보안(접속가능 IP)을 설정하시기 바랍니다.'). '</b>', 'parent.location.replace("manage_list.php")'); | |
| } catch (Exception $e) { | |
| throw new LayerNotReloadException($e->getMessage(), $e->getCode(), $e, null, 3000); | |
| } else { // 기존 운영자 권한 불러오기 | |
| $mode = 'modify'; | |
| } | |
| // 운영자 정보 | |
| if ($request->get()->get('sno') == '') { | |
| $this->setData('disabled', $disabled); | |
| $this->setData('changeType', $request->get()->get('type')); | |
| $this->setData('isAccessControlReason', $request->get()->get('isAccessControlReason')); | |
| $this->setData('sno', $request->get()->get('sno')); | |
| break; | |
| // --- 운영자 권한 설정> 운영자 검색 | |
| gd_isset($getValue['pageNum'], 40); | |
| $getData = $_managerClass->getManagerList($getValue); | |
| $page = \App::load('\\Component\\Page\\Page', $getValue['page']); // 페이지 재설정 | |
| // --- 관리자 디자인 템플릿 | |
| // --- 운영자 권한 설정 저장 | |
| case 'setManagePermission': | |
| try { | |
| if ($this->getData('managerPermissionMethodExist') !== true || $this->getData('adminMenuPermissionMethodExist') !== true) { | |
| throw new \RuntimeException(__('정상적으로 서비스가 제공되지 않습니다. 잠시 후 다시 시도해주세요.<br>문제가 지속될 경우 1:1문의 게시판에 문의해주세요.')); | |
| } | |
| $_managerClass->saveManagersPermission($request->post()->toArray()); | |
| $this->layerNotReload(__('저장이 완료되었습니다.'), 'parent.save_after_manger();'); | |
| } catch (Exception $e) { | |
| throw new LayerNotReloadException($e->getMessage(), null, null, null, 5000); | |
| case 'authSms': | |
| $session->del(Member::SESSION_USER_CERTIFICATION); | |
| $smsPoint = Sms::getPoint(); | |
| if ($smsPoint < 1) { | |
| $this->json( | |
| [ | |
| 'error' => 100, | |
| 'message' => __('SMS 포인트가 부족합니다. 인증번호 발송을 위해 SMS 포인트를 충전해주세요'), | |
| ] | |
| ); | |
| } | |
| // 인증번호 생성 | |
| $userCertificationSession['certificationCode'] = Otp::getOtp(8); | |
| $cellPhone = $request->post()->get('cellPhone'); | |
| $receiver[] = ['cellPhone' => $cellPhone]; | |
| $logData['reserve']['dbTable'] = $userCertificationSession; | |
| $msg = sprintf(__('인증번호는 [%s]입니다. 정확히 입력해주세요.'), $userCertificationSession['certificationCode']); | |
| $smsSender = \App::load(SmsSender::class); | |
| $smsSender->validPassword(\App::load(\Component\Sms\SmsUtil::class)->getPassword()); | |
| $smsSender->setSmsPoint($smsPoint); | |
| $smsSender->setMessage(new SmsMessage($msg)); | |
| $smsSender->setSmsType('user'); | |
| $smsSender->setMsgType('auth'); //인증용 | |
| $smsSender->setReceiver($receiver); | |
| $smsSender->setLogData(['disableResend' => true]); | |
| $smsSender->setContentsMask([$userCertificationSession['certificationCode']]); | |
| $smsResult[] = $smsSender->send(); | |
| if ($smsResult[0]['success'] === 1) { | |
| $userCertificationSession['authType'] = 'authSms'; | |
| $certData = [ | |
| 'certNo' => $userCertificationSession['certificationCode'], | |
| ] | |
| ); | |
| } | |
| $exitMessage = '등록된 SMS 발신번호가 없어 인증번호를 SMS로 발송할 수 없습니다.'; | |
| $exitMessage .= ' 발신번호 사전등록제에 따라 SMS를 발송하려면 발신번호가 먼저 등록되어 있어야 합니다.'; | |
| $exitMessage .= '<br />고도몰 홈페이지의 마이페이지>SMS발신번호 등록/관리에서 발신번호 등록 후'; | |
| $exitMessage .= ' 관리자의 회원>SMS 관리>자동 SMS 설정에서 발신번호를 설정할 수 있습니다.'; | |
| $this->json( | |
| [ | |
| 'error' => 200, | |
| 'message' => __($exitMessage), | |
| ] | |
| ); | |
| break; | |
| break; | |
| } | |
| } | |
| } | |
변경 후ManagePsController.php
| use Component\Policy\ManageSecurityPolicy; | |
| use Component\Policy\Policy; | |
| use Component\Scm\ScmAdmin; | |
| use Exception; | |
| use Framework\Debug\Exception\LayerNotReloadException; | |
| use Framework\Security\Otp; | |
| use Framework\Utility\ComponentUtils; | |
| use Framework\Utility\StringUtils; | |
| use Framework\Security\Token; | |
| use Origin\Enum\Crm\Message\MessageOperatorRoleType; | |
| use Origin\Service\Crm\Message\MessageOperatorRoleGuard; | |
| use Origin\Service\Crm\Message\MessageOperatorRoleService; | |
| use Origin\Service\Member\ManagerEditPolicy; | |
| use Origin\Service\Sms\OccupancyAuth\OccupancyOtpSmsService; | |
| /** | |
| * 운영자 등록/수정 처리 페이지 | |
| $request = \App::getInstance('request'); | |
| $session = \App::getInstance('session'); | |
| $_managerClass = \App::load(Manager::class); | |
| $mode = $request->request()->get('mode'); | |
| // 각 모드에 따른 처리 | |
| switch ($mode) { | |
| case 'modify': // --- 관리자 등록 / 수정 | |
| if (gd_is_provider()) { | |
| $managerData = $_managerClass->getManagerData($request->post()->get('sno')); | |
| $this->layer(__('잘못된 경로로 접근하셨습니다.'), 'parent.location.reload()'); | |
| } | |
| case 'register': | |
| $this->setManageMenuCode($mode); | |
| try { | |
| if ((DEFAULT_CODE_SCMNO === (int)$request->post()->get('sno')) && (DEFAULT_CODE_SCMNO !== (int)$session->get('manager.sno'))) { | |
| $this->layer(__('잘못된 경로로 접근하셨습니다.'), 'parent.location.reload()'); | |
| } | |
| // 데모인 경우 패스워드 수정 금지 | |
| $demo = \App::load(GodoDemoApi::class); | |
| if ($demo->checkDomain() === true && $mode === 'modify') { | |
| $request->post()->del('isModManagerPw'); | |
| } | |
| // SMS 발신관리위임자 지정/회수 — 버튼 선택(설정/해제) + 사유 입력 (책임자만). 저장 전엔 검증만, 실제 지정/회수(functionAuth messageSendSms 반영 포함)는 저장 후 Service 로 일원화. | |
| /** @var MessageOperatorRoleService $messageRoleService */ | |
| $messageRoleService = \App::getInstance(MessageOperatorRoleService::class); | |
| // 위임자/발송담당자 계정 정보는 책임자(최고운영자)와 본인만 수정 가능 — 메뉴 쓰기 권한보다 우선 | |
| if ($mode === 'modify') { | |
| $editTargetSno = (int)$request->post()->get('sno'); | |
| /** @var ManagerEditPolicy $managerEditPolicy */ | |
| $managerEditPolicy = \App::getInstance(ManagerEditPolicy::class); | |
| if (!$managerEditPolicy->canEditAccount($editTargetSno)) { | |
| throw new Exception(__('해당 계정 정보는 책임자 및 본인만 수정할 수 있습니다.')); | |
| } | |
| // 화면 잠금으로 값이 빠진 요청과 잠금 우회로 값이 실린 요청을 같은 방식으로 처리한다 | |
| $isSenderNameLocked = $managerEditPolicy->isSenderNameLocked($editTargetSno); | |
| $isSenderCellPhoneLocked = $managerEditPolicy->isSenderCellPhoneLocked($editTargetSno); | |
| if ($isSenderNameLocked || $isSenderCellPhoneLocked) { | |
| $senderRoleInfo = $_managerClass->getManagerInfo($editTargetSno); | |
| if ($isSenderNameLocked) { | |
| $request->post()->set('managerNm', $senderRoleInfo['managerNm']); | |
| } | |
| if ($isSenderCellPhoneLocked) { | |
| $request->post()->set('cellPhone', $senderRoleInfo['cellPhone']); | |
| } | |
| } | |
| if ($managerEditPolicy->isRepresentativeProfileLocked($editTargetSno)) { | |
| $superManagerInfo = $_managerClass->getManagerInfo($editTargetSno); | |
| $request->post()->set('managerNm', $superManagerInfo['managerNm']); | |
| $request->post()->set('cellPhone', $superManagerInfo['cellPhone']); | |
| $request->post()->set('email', $superManagerInfo['email']); | |
| } | |
| } | |
| $targetSno = (int)$request->post()->get('sno'); | |
| $messageDelegateAction = (string)$request->post()->get('messageDelegateAction'); | |
| $accessControlReason = trim((string)$request->post()->get('accessControlReason')); // 사유는 accessControlReason 으로 제출 → 운영자 관리/수정 개인정보접속기록에 남음 | |
| $postFunctionAuth = $request->post()->get('functionAuth'); | |
| // 권한 검증 실패는 [확인] 버튼으로 닫는 얼럿이어야 해 자동 닫힘 레이어를 쓰는 공통 catch 를 타지 않고 여기서 직접 출력한다 | |
| try { | |
| $messageRoleService->assertRoleChangeAllowed( | |
| $targetSno, | |
| $messageDelegateAction, | |
| $accessControlReason, | |
| $request->post()->get('permissionSetupMode') === 'managePermission', | |
| is_array($postFunctionAuth) ? $postFunctionAuth : [] | |
| ); | |
| if ($mode === 'register') { | |
| $messageRoleService->assertAssignableNewAccount( | |
| (string)$request->post()->get('scmNo'), | |
| (string)$request->post()->get('isSuper'), | |
| $messageDelegateAction | |
| ); | |
| } | |
| } catch (Exception $e) { | |
| $this->js('parent.dialog_alert("' . addslashes(__($e->getMessage())) . '","' . __('경고') . '" ,{isReload:false});'); | |
| } | |
| // 발송담당자 권한 전이 발행용 스냅샷 — 저장 후 상태와 비교해 실제로 바뀐 대상만 발행한다 | |
| $senderPermissionSnapshot = $messageRoleService->collectSenderPermissions([$targetSno]); | |
| if ($request->post()->get('permissionSetupMode') == 'managePermission' && $this->getData('managerPermissionMethodExist') === true && $this->getData('adminMenuPermissionMethodExist') === true) { | |
| $postData = $request->post()->toArray(); | |
| if (method_exists($_managerClass, 'setManagerWorkDebugPermissionFl')) { | |
| $_managerClass->saveManagerWritePermissionData($postData, $managerSno); | |
| $_managerClass->saveManagerIp($postData,$managerSno); | |
| } else { | |
| $managerSno = $_managerClass->saveManagerData($request->post()->toArray(), $request->files()->toArray()); | |
| } | |
| // 저장 후 반영은 Service 로 일원화 — 전이 발행과 위임자 지정/회수 순서가 여기에 고정돼 있다. 등록은 저장 후 확정된 sno. | |
| $messageRoleService->applyRoleChange( | |
| $targetSno > 0 ? $targetSno : (int)$managerSno, | |
| $messageDelegateAction, | |
| $accessControlReason, | |
| $senderPermissionSnapshot | |
| ); | |
| $this->layer(__('저장이 완료되었습니다.') . '<br/><b class="notice-danger notice-info">'.__('개인정보보호법에 따라 개인정보처리시스템에 접속하는') . '<br>'.__('개인정보취급자(운영자)인 경우에는 접속보안(접속가능 IP)을 설정하시기 바랍니다.'). '</b>', 'parent.location.replace("manage_list.php")'); | |
| } catch (Exception $e) { | |
| throw new LayerNotReloadException($e->getMessage(), $e->getCode(), $e, null, 3000); | |
| } else { // 기존 운영자 권한 불러오기 | |
| $mode = 'modify'; | |
| } | |
| $this->setManageMenuCode($mode); | |
| // 운영자 정보 | |
| if ($request->get()->get('sno') == '') { | |
| $this->setData('disabled', $disabled); | |
| $this->setData('changeType', $request->get()->get('type')); | |
| $this->setData('isAccessControlReason', $request->get()->get('isAccessControlReason')); | |
| /** @var MessageOperatorRoleService $messageOperatorRoleService */ | |
| $messageOperatorRoleService = \App::getInstance(MessageOperatorRoleService::class); | |
| $this->setData('sno', $request->get()->get('sno')); | |
| // 최고운영자/위임자면 발송담당자-SMS(messageSendSms) 체크 고정+잠금 (뷰에서 사용) | |
| $this->setData('isMessageApprover', $messageOperatorRoleService->isSenderPermissionLocked((int)$request->get()->get('sno'))); | |
| $this->setData('isEditorMessageApprover', $messageOperatorRoleService->isApprover((int)$session->get('manager.sno'))); | |
| /** @var MessageOperatorRoleGuard $messageOperatorRoleGuard */ | |
| $messageOperatorRoleGuard = \App::getInstance(MessageOperatorRoleGuard::class); | |
| $isEditorMessageRepresentative = $messageOperatorRoleGuard->isRepresentative(); | |
| $this->setData('isEditorMessageRepresentative', $isEditorMessageRepresentative); | |
| // 차단 조회는 외부 API 호출이라 책임자 화면에서만 수행한다 | |
| $this->setData('isCallerBlocked', $isEditorMessageRepresentative && $messageOperatorRoleService->isBlockedRepresentative()); | |
| $this->setData('messageSenderCount', $messageOperatorRoleService->countSenders()); | |
| $this->setData('messageSenderLimit', MessageOperatorRoleService::MAX_SENDER_COUNT); | |
| break; | |
| // --- 운영자 권한 설정> 운영자 검색 | |
| gd_isset($getValue['pageNum'], 40); | |
| $getData = $_managerClass->getManagerList($getValue); | |
| // SMS 권한 컬럼 라벨 (조회된 행 데이터만으로 판정 — 추가 쿼리 없음) | |
| /** @var MessageOperatorRoleService $messageSmsRoleService */ | |
| $messageSmsRoleService = \App::getInstance(MessageOperatorRoleService::class); | |
| if (is_array($getData['data'])) { | |
| foreach ($getData['data'] as $smsRoleRowKey => $smsRoleRow) { | |
| $smsRoleValue = $messageSmsRoleService->extractRole($smsRoleRow); | |
| $getData['data'][$smsRoleRowKey]['smsRoleName'] = MessageOperatorRoleType::getName($smsRoleValue); | |
| $getData['data'][$smsRoleRowKey]['smsRole'] = (string)$smsRoleValue; | |
| $getData['data'][$smsRoleRowKey]['smsPermissionLocked'] = $messageSmsRoleService->isSenderPermissionLockedByRow($smsRoleRow) ? 'y' : 'n'; | |
| } | |
| } | |
| $page = \App::load('\\Component\\Page\\Page', $getValue['page']); // 페이지 재설정 | |
| // --- 관리자 디자인 템플릿 | |
| // --- 운영자 권한 설정 저장 | |
| case 'setManagePermission': | |
| // 차단 조회 등 admin API 는 request 의 menuCode 를 헤더로 싣는다 | |
| // — PS 요청은 인터셉터가 채워주지 않으므로 실제 화면 메뉴로 직접 지정한다 | |
| $this->setMenuCode('policy', 'management', 'managePermission'); | |
| try { | |
| if ($this->getData('managerPermissionMethodExist') !== true || $this->getData('adminMenuPermissionMethodExist') !== true) { | |
| throw new \RuntimeException(__('정상적으로 서비스가 제공되지 않습니다. 잠시 후 다시 시도해주세요.<br>문제가 지속될 경우 1:1문의 게시판에 문의해주세요.')); | |
| } | |
| $permissionPostData = $request->post()->toArray(); | |
| /** @var MessageOperatorRoleService $senderRoleService */ | |
| $senderRoleService = \App::getInstance(MessageOperatorRoleService::class); | |
| // 벌크 저장은 여러 운영자의 발송담당자 권한을 한 번에 바꾸므로 저장 전 상태를 모아 전이 대상만 1건으로 발행한다 | |
| $senderPermissionTargetSnos = is_array($permissionPostData['manage_sno'] ?? null) ? $permissionPostData['manage_sno'] : []; | |
| $senderPermissionSnapshot = $senderRoleService->collectSenderPermissions($senderPermissionTargetSnos); | |
| $_managerClass->saveManagersPermission($permissionPostData); | |
| $revokedSenderSnos = $senderRoleService->publishSenderPermissionTransition( | |
| $senderPermissionTargetSnos, | |
| $senderPermissionSnapshot, | |
| trim((string)($permissionPostData['accessControlReason'] ?? '')) | |
| ); | |
| $senderRoleService->destroyCertificationIdentifiers($revokedSenderSnos); | |
| $this->layerNotReload(__('저장이 완료되었습니다.'), 'parent.save_after_manger();'); | |
| } catch (Exception $e) { | |
| throw new LayerNotReloadException($e->getMessage(), null, null, null, 5000); | |
| case 'authSms': | |
| $session->del(Member::SESSION_USER_CERTIFICATION); | |
| $userCertificationSession['certificationCode'] = Otp::getOtp(8); | |
| $cellPhone = $request->post()->get('cellPhone'); | |
| $isSent = \App::getInstance(OccupancyOtpSmsService::class) | |
| ->sendCertificationCode($cellPhone, $userCertificationSession['certificationCode']); | |
| if ($isSent) { | |
| $userCertificationSession['authType'] = 'authSms'; | |
| $certData = [ | |
| 'certNo' => $userCertificationSession['certificationCode'], | |
| ] | |
| ); | |
| } | |
| $this->json( | |
| [ | |
| 'error' => 200, | |
| 'message' => __('인증번호 SMS 발송에 실패했습니다. 잠시 후 다시 시도해주세요.'), | |
| ] | |
| ); | |
| break; | |
| break; | |
| } | |
| } | |
| /** | |
| * 차단 조회 등 admin API 는 request 의 menuCode 를 헤더로 싣는데 | |
| * PS 요청은 인터셉터가 채워주지 않으므로 운영자 등록·수정 화면 메뉴로 직접 지정한다 | |
| */ | |
| protected function setManageMenuCode(string $mode) | |
| { | |
| switch ($mode) { | |
| case 'modify': | |
| $this->setMenuCode('policy', 'management', 'modify'); | |
| break; | |
| default: | |
| $this->setMenuCode('policy', 'management', 'register'); | |
| break; | |
| } | |
| } | |
| } | |
변경 전ManageRegisterController.php
| use Framework\Debug\Exception\AlertBackException; | |
| use Framework\Security\Token; | |
| use Framework\StaticProxy\Proxy\App; | |
| use Origin\Util\Oauth\Commerce\CommerceLoginUtils; | |
| use Globals; | |
| use Request; | |
| if ($mode === 'register') { | |
| $this->callMenu('policy', 'management', 'register'); | |
| } elseif ($isModify) { | |
| $this->callMenu('policy', 'management', 'modify'); | |
| } | |
| // 메일도메인 | |
| // 최고운영자 커머스 통합회원 로그인 연동 패치일 이후 설치 상점인지 | |
| $isMallInstalledAfterPatch = CommerceLoginUtils::isMallInstalledAfterPatch(); | |
| // --- 관리자 디자인 템플릿 | |
| $this->setData('isPasswordChangeRestricted', $isCompanySuperManager && $isMallInstalledAfterPatch); | |
| $this->setData('isgPlusGift', $isgPlusGift); | |
변경 후ManageRegisterController.php
| use Framework\Debug\Exception\AlertBackException; | |
| use Framework\Security\Token; | |
| use Framework\StaticProxy\Proxy\App; | |
| use Origin\Service\Crm\Message\MessageOperatorRoleGuard; | |
| use Origin\Service\Crm\Message\MessageOperatorRoleService; | |
| use Origin\Service\Member\ManagerEditPolicy; | |
| use Origin\Service\Member\ManagerService; | |
| use Origin\Util\Oauth\Commerce\CommerceLoginUtils; | |
| use Globals; | |
| use Request; | |
| if ($mode === 'register') { | |
| $this->callMenu('policy', 'management', 'register'); | |
| // 차단 조회 등 admin API 는 request 의 menuCode 를 헤더로 싣는데 | |
| // 인터셉터는 발송 게이트 메뉴만 채워주므로 여기서 직접 지정한다 | |
| $this->setMenuCode('policy', 'management', 'register'); | |
| } elseif ($isModify) { | |
| $this->callMenu('policy', 'management', 'modify'); | |
| $this->setMenuCode('policy', 'management', 'modify'); | |
| } | |
| // 메일도메인 | |
| // 최고운영자 커머스 통합회원 로그인 연동 패치일 이후 설치 상점인지 | |
| $isMallInstalledAfterPatch = CommerceLoginUtils::isMallInstalledAfterPatch(); | |
| // SMS 발신 위임자 권한 (권한 설정 섹션 row + [위임자 권한 설정]/[위임자 권한 해제] 버튼 + 사유) | |
| // 규칙(뷰어 공급사·대상 적격성·역할)은 Origin(Guard/Service)에 위임 — 튜닝돼도 규칙 유지. 컨트롤러는 뷰데이터 조립만. | |
| /** @var MessageOperatorRoleGuard $messageOperatorRoleGuard */ | |
| $messageOperatorRoleGuard = \App::getInstance(MessageOperatorRoleGuard::class); | |
| /** @var MessageOperatorRoleService $messageOperatorRoleService */ | |
| $messageOperatorRoleService = \App::getInstance(MessageOperatorRoleService::class); | |
| /** @var ManagerEditPolicy $managerEditPolicy */ | |
| $managerEditPolicy = \App::getInstance(ManagerEditPolicy::class); | |
| $messageDelegate = ['visible' => false, 'editable' => false, 'assigned' => false]; | |
| $managerNo = (int)$data['data']['sno']; | |
| $scmNo = (int)$data['data']['scmNo']; | |
| // 등록(통합회원 연동 + 본인인증)을 마치지 않은 최고운영자는 지정/회수 주체가 아니므로 UI 자체를 내린다 — 저장측 차단만으로는 안내 없는 실패가 된다 | |
| $isRegisteredRepresentative = $messageOperatorRoleGuard->isRepresentative() | |
| && $messageOperatorRoleGuard->isRepresentativeConnected(); | |
| // 노출 대상: 발신관리책임자만 + 뷰어가 공급사 아님 + (등록이거나, 수정 대상이 본사 일반운영자) | |
| if ($isRegisteredRepresentative | |
| && $messageOperatorRoleGuard->isProviderContext() === false | |
| && (!$isModify || $messageOperatorRoleService->isEligibleDelegateTarget($managerNo, $scmNo))) { | |
| $delegate = $messageOperatorRoleService->getDelegate(); | |
| $assigned = $isModify && $delegate !== null && $delegate->getSno() === $managerNo; | |
| $messageDelegate = [ | |
| 'visible' => true, | |
| 'editable' => $isRegisteredRepresentative, // 지정/회수는 발신관리책임자만 | |
| 'assigned' => $assigned, | |
| 'blockedByExisting' => $delegate !== null && $delegate->getSno() !== $managerNo, // 다른 운영자가 이미 위임자 → 설정 클릭 시 안내 모달 | |
| // 노출 대상일 때만 조회 — 차단 여부는 외부 API 호출이라 비노출 화면에서 불필요한 호출을 만들지 않는다 | |
| 'callerBlocked' => $messageOperatorRoleService->isBlockedRepresentative(), | |
| ]; | |
| } | |
| $this->setData('messageDelegate', $messageDelegate); | |
| // 발송 역할 계정(책임자·위임자·발송담당자)의 휴대폰번호는 본인인증(DI 주입)으로 처리한다 | |
| // 본인인증은 본인 계정에서만 성립하므로 책임자·제3자가 여는 화면은 기존 점유인증(SMS) UI 를 그대로 쓴다 | |
| $isSelfTarget = $isModify && (int)$session->get('manager.sno') === $managerNo; | |
| // 최고운영자·발송 역할 계정은 정보 수정 주체를 책임자와 본인으로 제한한다 | |
| $isRestrictedEditTarget = $isModify | |
| && ($managerNo === ManagerService::SUPER_MANAGER_SNO | |
| || $messageOperatorRoleService->hasSenderPermission($managerNo)); | |
| // 최고운영자의 이름·휴대폰번호·이메일은 통합회원 정보 동기화 또는 발신관리책임자 등록 이후 어드민 입력이 기준이 아니다 — 그때부터 화면 입력을 잠그고 통합 마이페이지 안내를 노출한다 | |
| $isRepresentativeSelf = $isModify && $managerEditPolicy->isRepresentativeProfileLocked($managerNo); | |
| $this->setData('superManagerCommerceLinked', $isRepresentativeSelf); | |
| $selfCertification = ['enabled' => false]; | |
| // 최고운영자의 본인인증 제공 기준은 잠금과 달라(동기화 후에는 미제공) 정책 판정을 따로 받는다 — 등록 전에는 점유인증으로 정보를 맞춘다 | |
| $isSelfCertificationProvided = $managerNo === ManagerService::SUPER_MANAGER_SNO | |
| ? $managerEditPolicy->isRepresentativeSelfCertificationProvided($managerNo) | |
| : $isRestrictedEditTarget; | |
| if ($isSelfTarget && $isSelfCertificationProvided) { | |
| $selfCertification = [ | |
| 'enabled' => true, | |
| 'certified' => $messageOperatorRoleGuard->isConnected($managerNo), | |
| 'isSelf' => $isSelfTarget, | |
| ]; | |
| $this->addScript([ | |
| 'manager-certification.js?' . time(), | |
| ]); | |
| } | |
| $this->setData('selfCertification', $selfCertification); | |
| // 인증 방식 분기와 분리해 둔다 — 본인인증 화면이 본인 한정이 되어도 책임자·제3자 화면의 1차 차단은 남아야 한다 | |
| // 저장측 throw 는 발송 역할 계정만 막으므로 최고운영자 대상은 이 화면 판정이 유일한 차단이다 | |
| $this->setData( | |
| 'managerInfoModifiable', | |
| !$isRestrictedEditTarget || $messageOperatorRoleGuard->isRepresentative() || $isSelfTarget | |
| ); | |
| // 동기화 전에만 통합회원 연동 안내를 노출한다 — 동기화 후에는 잠긴 필드 하단 안내가 대신한다 | |
| $this->setData('superManagerCommerceGuide', $isModify && $managerEditPolicy->isRepresentativeProfileUnsynced($managerNo)); | |
| // 이름·휴대폰번호는 본인인증 명의라 직접 수정할 수 없다 — 발송 역할 계정 본인 화면과 책임자 본인 화면에서 잠근다 | |
| // 이름은 발송 역할 계정이면 책임자에게도 잠기고, 휴대폰번호는 책임자에게만 열린다 | |
| $this->setData('senderNameLocked', ($isModify && $managerEditPolicy->isSenderNameLocked($managerNo)) || $isRepresentativeSelf); | |
| $this->setData('senderCellPhoneLocked', ($isModify && $managerEditPolicy->isSenderCellPhoneLocked($managerNo)) || $isRepresentativeSelf); | |
| // --- 관리자 디자인 템플릿 | |
| $this->setData('isPasswordChangeRestricted', $isCompanySuperManager && $isMallInstalledAfterPatch); | |
| $this->setData('isgPlusGift', $isgPlusGift); | |
변경 전ManageSecurityController.php
| namespace Bundle\Controller\Admin\Policy; | |
| use Component\Sms\Sms; | |
| use Exception; | |
| use Framework\Debug\Exception\LayerException; | |
| use Framework\Utility\StringUtils; | |
| } | |
| gd_isset(asort($dataSecurity['countryAccessBlocking']), ''); | |
| if ($superMangerInfo['data']['isSmsAuth'] == 'y' && Sms::getPoint() > 0) { | |
| $dataSecurity['superCellPhoneFl'] = true; | |
| } else { | |
| $dataSecurity['superCellPhoneFl'] = false; | |
변경 후ManageSecurityController.php
| namespace Bundle\Controller\Admin\Policy; | |
| use Exception; | |
| use Framework\Debug\Exception\LayerException; | |
| use Framework\Utility\StringUtils; | |
| } | |
| gd_isset(asort($dataSecurity['countryAccessBlocking']), ''); | |
| if ($superMangerInfo['data']['isSmsAuth'] == 'y') { | |
| $dataSecurity['superCellPhoneFl'] = true; | |
| } else { | |
| $dataSecurity['superCellPhoneFl'] = false; | |
변경 전CartRemindListController.php
| namespace Bundle\Controller\Admin\Promotion; | |
| use Component\CartRemind\CartRemind; | |
| use Component\Godo\GodoSmsServerApi; | |
| use Component\Sms\Sms; | |
| use Component\Sms\SmsAdmin; | |
| use Framework\Utility\ConfigUrlUtils; | |
| use Request; | |
| /** | |
| // --- 메뉴 설정 | |
| $this->callMenu('promotion', 'cartRemind', 'cartRemindList'); | |
| $smsAdmin = new SmsAdmin(); | |
| $smsAutoData = $smsAdmin->getSmsAutoData(); | |
| // SMS 발신번호 사전 등록 번호 정보 | |
| $godoSms = new GodoSmsServerApi(); | |
| $smsPreRegister = $godoSms->checkSmsCallNumber($smsAutoData['smsCallNum']); | |
| $searchData = Request::get()->all(); | |
| $cartRemind = new CartRemind(); | |
| $this->setData('smsAutoList', Sms::SMS_AUTO_RECEIVE_LIST); | |
| $this->setData('smsAutoOrderPeriod', Sms::SMS_AUTO_ORDER_PERIOD); | |
| $this->setData('smsAutoData', gd_htmlspecialchars($smsAutoData)); | |
| $this->setData('smsPreRegister', $smsPreRegister); | |
| $this->setData('cartRemindData', gd_isset($getData['data'])); | |
| $this->setData('convertArrData', gd_isset($convertArrData)); | |
| $this->setData('page', $page); | |
| $this->setData('commerceSmsIntroUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('sms-intro')); | |
| $this->setData('commerceShopMainUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('shop-main')); | |
| } | |
| } | |
변경 후CartRemindListController.php
| namespace Bundle\Controller\Admin\Promotion; | |
| use Component\CartRemind\CartRemind; | |
| use Component\Sms\Sms; | |
| use Framework\Utility\ConfigUrlUtils; | |
| use Origin\Enum\Crm\Message\LegacyCallNumberStatus; | |
| use Origin\Service\Crm\Message\SmsManageCallNumberService; | |
| use Request; | |
| /** | |
| // --- 메뉴 설정 | |
| $this->callMenu('promotion', 'cartRemind', 'cartRemindList'); | |
| /** @var SmsManageCallNumberService $smsManageCallNumberService */ | |
| $smsManageCallNumberService = \App::getInstance(SmsManageCallNumberService::class); | |
| $smsCallNum = $smsManageCallNumberService->getCallNumber(); | |
| // 스킨이 === 로 4분기하고 else 가 없어, 키를 빼면 발신번호 영역이 통째로 렌더되지 않는다 | |
| $smsPreRegister = LegacyCallNumberStatus::toLegacyValue($smsManageCallNumberService->getLegacyCallNumberStatus()); | |
| $searchData = Request::get()->all(); | |
| $cartRemind = new CartRemind(); | |
| $this->setData('smsAutoList', Sms::SMS_AUTO_RECEIVE_LIST); | |
| $this->setData('smsAutoOrderPeriod', Sms::SMS_AUTO_ORDER_PERIOD); | |
| // 스킨은 smsAutoData['smsCallNum'] 만 참조하므로 배열 계약만 유지하고 자동발송 전건 조회는 하지 않는다 | |
| $this->setData('smsAutoData', gd_htmlspecialchars(['smsCallNum' => $smsCallNum])); | |
| $this->setData('smsPreRegister', $smsPreRegister); | |
| $this->setData('cartRemindData', gd_isset($getData['data'])); | |
| $this->setData('convertArrData', gd_isset($convertArrData)); | |
| $this->setData('page', $page); | |
| $this->setData('commerceShopMainUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('shop-main')); | |
| } | |
| } | |
변경 전ComebackCouponListController.php
| namespace Bundle\Controller\Admin\Promotion; | |
| use Exception; | |
| use Component\Godo\GodoSmsServerApi; | |
| use Component\Sms\SmsAdmin; | |
| use Framework\Debug\Exception\LayerException; | |
| use Framework\Utility\ConfigUrlUtils; | |
| use Request; | |
| class ComebackCouponListController extends \Controller\Admin\Controller | |
| // --- 모듈 호출 | |
| try { | |
| $smsAdmin = new SmsAdmin(); | |
| $smsAutoData = $smsAdmin->getSmsAutoData(); | |
| // SMS 발신번호 사전 등록 번호 정보 | |
| $godoSms = new GodoSmsServerApi(); | |
| $smsPreRegister = $godoSms->checkSmsCallNumber($smsAutoData['smsCallNum']); | |
| // 리스트 정보가져오기 | |
| $couponAdmin = \App::load('\\Component\\Coupon\\CouponAdmin'); | |
| } | |
| // --- 관리자 디자인 템플릿 | |
| $this->setData('smsAutoData', gd_htmlspecialchars($smsAutoData)); | |
| $this->setData('smsPreRegister', $smsPreRegister); | |
| $this->setData('data', gd_isset($getData['data'])); | |
| $this->setData('list', gd_isset($getData['list'])); | |
| $this->setData('page', $page); | |
| $this->setData('commerceSmsIntroUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('sms-intro')); | |
| $this->setData('commerceShopMainUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('shop-main')); | |
| } | |
| } | |
변경 후ComebackCouponListController.php
| namespace Bundle\Controller\Admin\Promotion; | |
| use Exception; | |
| use Framework\Debug\Exception\LayerException; | |
| use Framework\Utility\ConfigUrlUtils; | |
| use Origin\Enum\Crm\Message\LegacyCallNumberStatus; | |
| use Origin\Service\Crm\Message\SmsManageCallNumberService; | |
| use Request; | |
| class ComebackCouponListController extends \Controller\Admin\Controller | |
| // --- 모듈 호출 | |
| try { | |
| /** @var SmsManageCallNumberService $smsManageCallNumberService */ | |
| $smsManageCallNumberService = \App::getInstance(SmsManageCallNumberService::class); | |
| $smsCallNum = $smsManageCallNumberService->getCallNumber(); | |
| // 스킨이 === 로 4분기하고 else 가 없어, 키를 빼면 발신번호 영역이 통째로 렌더되지 않는다 | |
| $smsPreRegister = LegacyCallNumberStatus::toLegacyValue($smsManageCallNumberService->getLegacyCallNumberStatus()); | |
| // 리스트 정보가져오기 | |
| $couponAdmin = \App::load('\\Component\\Coupon\\CouponAdmin'); | |
| } | |
| // --- 관리자 디자인 템플릿 | |
| // 스킨은 smsAutoData['smsCallNum'] 만 참조하므로 배열 계약만 유지하고 자동발송 전건 조회는 하지 않는다 | |
| $this->setData('smsAutoData', gd_htmlspecialchars(['smsCallNum' => $smsCallNum])); | |
| $this->setData('smsPreRegister', $smsPreRegister); | |
| $this->setData('data', gd_isset($getData['data'])); | |
| $this->setData('list', gd_isset($getData['list'])); | |
| $this->setData('page', $page); | |
| $this->setData('commerceShopMainUrl', ConfigUrlUtils::getNhnCommerceMyGodoUrl('shop-main')); | |
| } | |
| } | |
변경 전LayerExcelAuthController.php
| namespace Bundle\Controller\Admin\Share; | |
| use Component\Sms\Sms; | |
| use Framework\Utility\StringUtils; | |
| /** | |
| ]; | |
| $isExcelAuthSms = ($auth['requireExcelAuthSms']) && $manager['isSmsAuth'] == 'y' && $manager['cellPhone']; | |
| $isExcelAuthEmail = $auth['requireExcelAuthEmail'] && $manager['isEmailAuth'] == 'y' && $manager['email']; | |
| $hasSmsPoint = (Sms::getPoint() >= 1); | |
| $getValue = \Request::get()->all(); | |
| if ($isExcelAuthSms) { | |
| if ($hasSmsPoint) { | |
| $securitySelect['authSms'] = '휴대폰'; | |
| $phoneArr = explode('-', $manager['cellPhone']); | |
| $phoneLen = strlen($phoneArr[1]); | |
| $s = ''; | |
| for ($i = 1; $i <= $phoneLen; $i++) { | |
| $s .= '*'; | |
| } | |
| $phoneArr[1] = $s; | |
| $phoneArr[2] = '**' . substr($phoneArr[2], 2, 2); | |
| $authInformation['cellPhone'] = implode('-', $phoneArr); | |
| } else { | |
| if ($isExcelAuthEmail) { | |
| $this->setData('message', __('잔여 SMS포인트가 부족하여 이메일로 인증을 진행합니다.')); | |
| } else { | |
| $this->setData('superAlertMessage', __('SMS 잔여 포인트가 부족하여 SMS 인증을 할 수 없습니다. 포인트를 충전해주시기 바랍니다.')); | |
| } | |
| } | |
| } | |
| if ($auth['requireExcelAuthSmsGodo']) { | |
| $securitySelect['authSmsGodo'] = '고도회원 휴대폰번호'; | |
변경 후LayerExcelAuthController.php
| namespace Bundle\Controller\Admin\Share; | |
| use Framework\Utility\StringUtils; | |
| /** | |
| ]; | |
| $isExcelAuthSms = ($auth['requireExcelAuthSms']) && $manager['isSmsAuth'] == 'y' && $manager['cellPhone']; | |
| $isExcelAuthEmail = $auth['requireExcelAuthEmail'] && $manager['isEmailAuth'] == 'y' && $manager['email']; | |
| $getValue = \Request::get()->all(); | |
| if ($isExcelAuthSms) { | |
| $securitySelect['authSms'] = '휴대폰'; | |
| $phoneArr = explode('-', $manager['cellPhone']); | |
| $phoneLen = strlen($phoneArr[1]); | |
| $s = ''; | |
| for ($i = 1; $i <= $phoneLen; $i++) { | |
| $s .= '*'; | |
| } | |
| $phoneArr[1] = $s; | |
| $phoneArr[2] = '**' . substr($phoneArr[2], 2, 2); | |
| $authInformation['cellPhone'] = implode('-', $phoneArr); | |
| } | |
| if ($auth['requireExcelAuthSmsGodo']) { | |
| $securitySelect['authSmsGodo'] = '고도회원 휴대폰번호'; | |
변경 전LayerManagePermissionController.php
| use Framework\Debug\Exception\LayerException; | |
| use Component\Admin\AdminMenu; | |
| use Component\Member\Manager; | |
| use Globals; | |
| /** | |
| $request = \App::getInstance('request'); | |
| $session = \App::getInstance('session'); | |
| $_managerClass = new Manager(); | |
| // 실행 조건 검증 | |
| $checked['permissionFl'][$existingPermission['permissionFl']] = 'checked="checked"'; | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| $this->getView()->setPageName('share/layer_manage_permission.php'); | |
| $this->setData('checked', $checked); | |
| $this->setData('disabled', $disabled); | |
| $this->setData('sno', $request->post()->get('sno')); | |
| $this->setData('scmFl', $scmFl); | |
| $this->setData('scmNo', $scmNo); | |
| $this->setData('viewMemuDepth', $viewMemuDepth); | |
변경 후LayerManagePermissionController.php
| use Framework\Debug\Exception\LayerException; | |
| use Component\Admin\AdminMenu; | |
| use Component\Member\Manager; | |
| use Origin\Service\Crm\Message\MessageOperatorRoleGuard; | |
| use Origin\Service\Crm\Message\MessageOperatorRoleService; | |
| use Globals; | |
| /** | |
| $request = \App::getInstance('request'); | |
| $session = \App::getInstance('session'); | |
| // 차단 조회 등 admin API 는 request 의 menuCode 를 헤더로 싣는다 | |
| // — 레이어 요청은 인터셉터가 채워주지 않으므로 실제 화면 메뉴로 직접 지정한다 | |
| $this->setMenuCode('policy', 'management', $request->post()->get('mode') === 'register' ? 'register' : 'modify'); | |
| $_managerClass = new Manager(); | |
| // 실행 조건 검증 | |
| $checked['permissionFl'][$existingPermission['permissionFl']] = 'checked="checked"'; | |
| // 최고운영자/위임자면 발송담당자-SMS(messageSendSms) 체크 고정+잠금 (레이어 최초 렌더에도 반영) | |
| /** @var MessageOperatorRoleService $messageOperatorRoleService */ | |
| $messageOperatorRoleService = \App::getInstance(MessageOperatorRoleService::class); | |
| $isMessageApprover = $messageOperatorRoleService->isSenderPermissionLocked((int)$request->post()->get('sno')); | |
| /** @var MessageOperatorRoleGuard $messageOperatorRoleGuard */ | |
| $messageOperatorRoleGuard = \App::getInstance(MessageOperatorRoleGuard::class); | |
| $isEditorMessageRepresentative = $messageOperatorRoleGuard->isRepresentative(); | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| $this->getView()->setPageName('share/layer_manage_permission.php'); | |
| $this->setData('checked', $checked); | |
| $this->setData('disabled', $disabled); | |
| $this->setData('sno', $request->post()->get('sno')); | |
| $this->setData('isMessageApprover', $isMessageApprover); | |
| $this->setData('isEditorMessageApprover', $messageOperatorRoleService->isApprover((int)$session->get('manager.sno'))); | |
| $this->setData('isEditorMessageRepresentative', $isEditorMessageRepresentative); | |
| // 차단 조회는 외부 API 호출이라 책임자 화면에서만 수행한다 | |
| $this->setData('isCallerBlocked', $isEditorMessageRepresentative && $messageOperatorRoleService->isBlockedRepresentative()); | |
| $this->setData('messageSenderCount', $messageOperatorRoleService->countSenders()); | |
| $this->setData('messageSenderLimit', MessageOperatorRoleService::MAX_SENDER_COUNT); | |
| $this->setData('scmFl', $scmFl); | |
| $this->setData('scmNo', $scmNo); | |
| $this->setData('viewMemuDepth', $viewMemuDepth); | |
변경 전layer_sms_template_approval_history.php
변경 후layer_sms_template_approval_history.php
| <table class="table table-rows table-fixed"> | |
| <colgroup> | |
| <col class="width10p"/> | |
| <col class="width30p"/> | |
| <col class="width30p"/> | |
| <col class="width30p"/> | |
| </colgroup> | |
| <thead> | |
| <tr> | |
| <th>번호</th> | |
| <th>처리 일시</th> | |
| <th>처리 내용</th> | |
| <th>처리자</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <?php if (count($history) > 0): ?> | |
| <?php foreach ($history as $log): ?> | |
| <tr class="text-center"> | |
| <td class="number"><?= $log['number'] ?></td> | |
| <td class="number"><?= $log['processedAt'] ?></td> | |
| <td><?= $log['eventName'] ?></td> | |
| <td><?= $log['processedBy'] ?></td> | |
| </tr> | |
| <?php endforeach; ?> | |
| <?php else: ?> | |
| <tr> | |
| <td colspan="4" class="no-data">승인 이력이 없습니다.</td> | |
| </tr> | |
| <?php endif; ?> | |
| </tbody> | |
| </table> | |
| <div class="text-center"> | |
| <button type="button" class="btn btn-lg btn-black" onclick="layer_close()">닫기</button> | |
| </div> | |
변경 전layer_sms_template_auto_send.php
변경 후layer_sms_template_auto_send.php
| <table class="table table-rows table-fixed"> | |
| <colgroup> | |
| <col class="width10p"/> | |
| <col class="width50p"/> | |
| <col class="width40p"/> | |
| </colgroup> | |
| <thead> | |
| <tr> | |
| <th>번호</th> | |
| <th>제목</th> | |
| <th>발송가능대상</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <?php if (count($autoSendList) > 0): ?> | |
| <?php foreach ($autoSendList as $index => $autoSend): ?> | |
| <tr class="text-center"> | |
| <td class="number"><?= $index + 1 ?></td> | |
| <td><?= $autoSend['sendItemName'] ?></td> | |
| <td><?= $autoSend['sendTargetName'] ?></td> | |
| </tr> | |
| <?php endforeach; ?> | |
| <?php else: ?> | |
| <tr> | |
| <td colspan="3" class="no-data">사용 중인 자동 알림이 없습니다.</td> | |
| </tr> | |
| <?php endif; ?> | |
| </tbody> | |
| </table> | |
| <div class="text-center"> | |
| <button type="button" class="btn btn-lg btn-black" onclick="layer_close()">닫기</button> | |
| </div> | |
변경 전layer_sms_template_form.php
변경 후layer_sms_template_form.php
| <?php | |
| $isModify = empty($template) === false; | |
| $category = $isModify ? $template['templateCategory'] : $defaultCategory; | |
| $templateName = $isModify ? (string) $template['templateName'] : ''; | |
| $nameMaxLengthAttr = mb_strlen($templateName) > $nameMaxLength ? '' : 'maxlength="' . $nameMaxLength . '"'; | |
| ?> | |
| <script src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-emoji.js') ?>"></script> | |
| <form id="frmSmsTemplate" method="post"> | |
| <input type="hidden" name="mode" value="<?= $isModify ? 'modify' : 'register' ?>"/> | |
| <input type="hidden" name="sno" value="<?= $isModify ? $template['sno'] : '' ?>"/> | |
| <table class="table table-cols"> | |
| <colgroup> | |
| <col class="width-md"/> | |
| <col/> | |
| </colgroup> | |
| <tbody> | |
| <?php if ($isModify): ?> | |
| <tr> | |
| <th>템플릿 코드</th> | |
| <td><?= $template['templateCode'] ?></td> | |
| </tr> | |
| <?php endif; ?> | |
| <tr> | |
| <th>카테고리 <span class="text-red">*</span></th> | |
| <td><?= gd_select_box(null, 'templateCategory', $categoryList, '', $category, null, null, 'form-control js-template-category') ?></td> | |
| </tr> | |
| <tr> | |
| <th>템플릿명 <span class="text-red">*</span></th> | |
| <td> | |
| <input type="text" name="templateName" class="form-control width-xl js-template-name" <?= $nameMaxLengthAttr ?> | |
| value="<?= $templateName ?>"/> | |
| <div class="notice-info">템플릿명은 템플릿 구분을 위한 입력 항목이며, 발송 내용에 포함되지 않습니다.</div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th>템플릿 내용 <span class="text-red">*</span></th> | |
| <td class="form-inline"> | |
| <div class="row"> | |
| <div class="col-xs-6 pdr0"> | |
| <span class="notice-info js-sms-type">SMS : 건당 1포인트 차감</span> | |
| <span class="notice-danger js-lms-type display-none">LMS : 건당 <?= $lmsPoint ?>포인트 차감</span> | |
| <button type="button" class="btn btn-white btn-sm pull-right js-toggle-replace-code">치환코드 보기</button> | |
| </div> | |
| </div> | |
| <div class="row pdt5"> | |
| <div class="col-xs-6 pdr0"> | |
| <input type="text" name="messageTitle" class="form-control width100p js-template-message-title" placeholder="제목을 입력해주세요." | |
| value="<?= $isModify ? $template['messageTitle'] : '' ?>" disabled="disabled"/> | |
| <div class="mgt5"> | |
| <input type="text" class="form-control width-3xs js-template-message-title-byte" value="0" readonly="readonly"/> / <?= $lmsSubjectLimit ?> Bytes | |
| </div> | |
| <textarea name="contents" rows="13" class="form-control width100p mgt5 js-template-contents"><?= $isModify ? $template['contents'] : '' ?></textarea> | |
| <div class="mgt10 js-template-contents-limit"> | |
| <input type="text" class="form-control width-3xs js-template-contents-byte" value="0" readonly="readonly"/> | |
| / <span class="js-sms-type"><?= $smsStringLimit ?></span><span class="js-lms-type display-none"><?= number_format($contentsMaxByte) ?></span> Bytes | |
| </div> | |
| </div> | |
| <div class="col-xs-6 display-none js-replace-code-table"> | |
| <div class="sms-replace-code-area" style="height: auto; margin-left: 0;"> | |
| <?php include('_sms_replace_code_table.php'); ?> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="row"> | |
| <div class="col-xs-12"> | |
| <div class="notice-info">URL 삽입 시 [단축 URL] 앱을 이용하시면 짧고 간단하게 URL을 사용하실 수 있습니다. <a href="https://apps.godo.co.kr/apps/1519" class="btn-link" target="_blank">바로가기</a></div> | |
| <div class="notice-info">치환코드 사용 시 LMS로 발송될 수 있으며 일부 문자가 포인트 부족으로 발송되지 않을 수 있습니다.<br/>- 포인트 충전 후 <a href="../member/sms_log.php" class="btn-link" target="_blank">SMS 발송 내역 보기</a>에서 재발송하실 수 있습니다.</div> | |
| <div class="notice-info">본문 내용이 LMS인 경우에만 제목 입력이 가능하며, 제목 영역에는 직접 (광고) 문구를 입력하지 않도록 하고 광고 삽입이 필요한 경우 광고성 문구 추가 기능을 사용해야 합니다.</div> | |
| </div> | |
| </div> | |
| </td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <div class="notice-info">등록/수정한 템플릿은 승인 후 발송 가능합니다.</div> | |
| <div class="text-center"> | |
| <button type="button" class="btn btn-lg btn-black js-template-save">저장</button> | |
| <button type="button" class="btn btn-lg btn-white js-template-cancel">취소</button> | |
| </div> | |
| </form> | |
| <script type="text/javascript"> | |
| <!-- | |
| (function () { | |
| var $form = $('#frmSmsTemplate'); | |
| var $category = $form.find('.js-template-category'); | |
| var $contents = $form.find('.js-template-contents'); | |
| var $messageTitle = $form.find('.js-template-message-title'); | |
| var $replaceCodeTable = $form.find('.js-replace-code-table'); | |
| var replaceCodeGroups = <?= json_encode($replaceCodeGroups) ?>; | |
| var smsStringLimit = <?= $smsStringLimit ?>; | |
| var lmsSubjectLimit = <?= $lmsSubjectLimit ?>; | |
| var contentsMaxByte = <?= $contentsMaxByte ?>; | |
| var contentsMaxLength = <?= $contentsMaxLength ?>; | |
| var focusPosition = 0; | |
| function stringToByte(str) { | |
| var cnt = 0; | |
| for (var i = 0; i < str.length; i++) { | |
| var charCode = str.charCodeAt(i); | |
| if (charCode > 127) { | |
| cnt += 2; | |
| } else if (charCode !== 13) { | |
| cnt++; | |
| } | |
| } | |
| return cnt; | |
| } | |
| function refreshMessageTitle() { | |
| var text = $messageTitle.val(); | |
| while (stringToByte(text) > lmsSubjectLimit) { | |
| text = text.substring(0, text.length - 1); | |
| } | |
| $messageTitle.val(text); | |
| $form.find('.js-template-message-title-byte').val(stringToByte(text)); | |
| } | |
| function refreshByte() { | |
| var byte = stringToByte($contents.val()); | |
| var isLms = byte > smsStringLimit; | |
| $form.find('.js-template-contents-byte').val(byte).css('color', isLms ? '#FF0000' : ''); | |
| $form.find('.js-sms-type').toggleClass('display-none', isLms); | |
| $form.find('.js-lms-type').toggleClass('display-none', !isLms); | |
| $messageTitle.prop('disabled', !isLms); | |
| } | |
| function showReplaceCodeGroup() { | |
| var group = replaceCodeGroups[$category.val()] || 'member'; | |
| $replaceCodeTable.find('.replace-code-area').addClass('display-none') | |
| .filter('[data-type="' + group + '"]').removeClass('display-none'); | |
| } | |
| $contents.on('input keyup change', refreshByte); | |
| $contents.on('focusout', function () { | |
| focusPosition = this.selectionStart; | |
| }); | |
| $messageTitle.on('input keyup change', refreshMessageTitle); | |
| smsEmoji.bind('.js-template-contents', refreshByte); | |
| smsEmoji.bind('.js-template-message-title', refreshMessageTitle); | |
| smsEmoji.bind('.js-template-name'); | |
| $category.on('change', showReplaceCodeGroup); | |
| refreshByte(); | |
| // 저장된 제목이 제한을 넘을 수 있어 초기에는 절삭 없이 byte 표시만 갱신한다 | |
| $form.find('.js-template-message-title-byte').val(stringToByte($messageTitle.val())); | |
| showReplaceCodeGroup(); | |
| $form.find('.js-toggle-replace-code').click(function () { | |
| $replaceCodeTable.toggleClass('display-none'); | |
| $(this).text($replaceCodeTable.hasClass('display-none') ? '치환코드 보기' : '치환코드 닫기'); | |
| }); | |
| $replaceCodeTable.on('click', '.js-btn-insert', function () { | |
| var code = $(this).closest('tr').find('td:eq(0)').text(); | |
| var input = $contents.val(); | |
| $contents.val(input.slice(0, focusPosition) + code + input.slice(focusPosition)); | |
| focusPosition += code.length; | |
| $contents.focus(); | |
| $contents.get(0).selectionEnd = focusPosition; | |
| refreshByte(); | |
| }); | |
| $form.find('.js-template-cancel').click(function () { | |
| layer_close(); | |
| }); | |
| $form.find('.js-template-save').click(function () { | |
| var $templateName = $form.find('input[name="templateName"]'); | |
| var missing = []; | |
| if ($category.val() === '') { | |
| missing.push({element: $category, message: '카테고리를 선택해 주세요.'}); | |
| } | |
| if ($templateName.val().trim() === '') { | |
| missing.push({element: $templateName, message: '템플릿명을 입력해 주세요.'}); | |
| } | |
| if ($contents.val().trim() === '') { | |
| missing.push({element: $contents, message: '템플릿 내용을 입력해 주세요.'}); | |
| } | |
| if (missing.length > 0) { | |
| alert(missing.length > 1 ? '필수 입력 항목을 입력해 주세요.' : missing[0].message); | |
| missing[0].element.focus(); | |
| return; | |
| } | |
| if (stringToByte($contents.val()) > contentsMaxByte) { | |
| alert('템플릿 내용은 ' + contentsMaxByte + 'byte까지 입력할 수 있습니다.'); | |
| return; | |
| } | |
| if ($contents.val().length > contentsMaxLength) { | |
| alert('템플릿 내용은 ' + contentsMaxLength + '자까지 입력할 수 있습니다.'); | |
| return; | |
| } | |
| var formData = $form.serializeArray(); | |
| if ($messageTitle.prop('disabled')) { | |
| formData.push({name: 'messageTitle', value: $messageTitle.val()}); | |
| } | |
| $.ajax({ | |
| url: './sms_template_ps.php', | |
| type: 'post', | |
| dataType: 'json', | |
| data: $.param(formData), | |
| success: function (response) { | |
| dialog_alert(response.message, '정보', {isReload: true}); | |
| }, | |
| error: function (xhr) { | |
| var response = xhr.responseJSON; | |
| alert(response && response.message ? response.message : '처리중에 오류가 발생하였습니다.'); | |
| } | |
| }); | |
| }); | |
| })(); | |
| //--> | |
| </script> | |
변경 전layer_sms_template_load.php
변경 후layer_sms_template_load.php
| <div class="js-template-load"> | |
| <link rel="stylesheet" href="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'css/layer-sms-template.css') ?>"/> | |
| <div class="notice-info">템플릿 작성 및 수정은 템플릿 관리에서 가능합니다. <a href="./sms_template.php" class="btn-link" target="_blank">바로가기</a></div> | |
| <div class="notice-info mgb10">SMS/LMS 템플릿은 승인 상태에서만 발송이 가능합니다. (검수중·반려 상태의 템플릿은 발송이 제한됩니다)</div> | |
| <form class="js-template-load-form" onsubmit="return false;"> | |
| <table class="table table-cols"> | |
| <colgroup> | |
| <col class="width-md"/> | |
| <col/> | |
| </colgroup> | |
| <tbody> | |
| <tr> | |
| <th>템플릿 선택</th> | |
| <td class="form-inline"> | |
| <?= gd_select_box(null, 'category', $categoryList, '', $search['category'], '전체') ?> | |
| <input type="text" name="keyword" value="<?= $search['keyword'] ?>" class="form-control width-xl" placeholder="제목 또는 내용을 입력해주세요."/> | |
| <button type="button" class="btn btn-sm btn-gray js-template-load-search">검색</button> | |
| </td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </form> | |
| <table class="table table-rows table-fixed sms-template-load-list"> | |
| <colgroup> | |
| <col class="width10p"/> | |
| <col class="width30p"/> | |
| <col/> | |
| </colgroup> | |
| <thead> | |
| <tr> | |
| <th>선택</th> | |
| <th>제목</th> | |
| <th>내용</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <?php if (count($templates) > 0): ?> | |
| <?php foreach ($templates as $template): ?> | |
| <tr> | |
| <td class="text-center"> | |
| <input type="radio" name="loadTemplateSno" value="<?= $template['sno'] ?>" | |
| data-contents="<?= $template['contents'] ?>" data-message-title="<?= $template['messageTitle'] ?>"/> | |
| </td> | |
| <td><?= $template['subject'] ?></td> | |
| <td><?= nl2br($template['contents']) ?></td> | |
| </tr> | |
| <?php endforeach; ?> | |
| <?php else: ?> | |
| <tr> | |
| <td colspan="3" class="no-data">등록된 템플릿이 없습니다.</td> | |
| </tr> | |
| <?php endif; ?> | |
| </tbody> | |
| </table> | |
| <div class="center js-template-load-page"><?= $page->getPage('#') ?></div> | |
| <div class="text-center"> | |
| <button type="button" class="btn btn-lg btn-white js-template-load-cancel">취소</button> | |
| <button type="button" class="btn btn-lg btn-black js-template-load-apply">추가</button> | |
| </div> | |
| <script type="text/javascript"> | |
| <!-- | |
| (function () { | |
| var $layer = $('.js-template-load').last(); | |
| var RELOAD_FAIL_MESSAGE = '일시적인 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.'; | |
| /** | |
| * 세션 만료·진입 제한 응답은 레이어 HTML 이 아니어서 교체 대상이 비어 있다 | |
| */ | |
| function findLayer(html) { | |
| return $('<div>').html(html).find('.js-template-load'); | |
| } | |
| function failMessage(html) { | |
| var text = $.trim($('<div>').html(html).text()); | |
| return text !== '' && text.length <= 100 ? text : RELOAD_FAIL_MESSAGE; | |
| } | |
| function reload(page) { | |
| var params = $layer.find('.js-template-load-form').serializeArray(); | |
| params.push({name: 'page', value: page || 1}); | |
| $.post('./layer_sms_template_load.php', params, function (html) { | |
| var $next = findLayer(html); | |
| if ($next.length < 1) { | |
| alert(failMessage(html)); | |
| return; | |
| } | |
| $layer.replaceWith($next); | |
| }).fail(function () { | |
| alert(RELOAD_FAIL_MESSAGE); | |
| }); | |
| } | |
| $layer.find('.js-template-load-search').click(function () { | |
| reload(1); | |
| }); | |
| $layer.find('input[name="keyword"]').keypress(function (e) { | |
| if (e.which === 13) { | |
| reload(1); | |
| } | |
| }); | |
| $layer.find('.js-template-load-page').on('click', 'a', function (e) { | |
| e.preventDefault(); | |
| reload($(this).attr('data-page')); | |
| }); | |
| $layer.find('.js-template-load-cancel').click(function () { | |
| layer_close(); | |
| }); | |
| $layer.find('.js-template-load-apply').click(function () { | |
| var $selected = $layer.find('input[name="loadTemplateSno"]:checked'); | |
| if ($selected.length < 1) { | |
| alert('템플릿을 선택해 주세요.'); | |
| return; | |
| } | |
| gd_sms_template.apply($selected.attr('data-contents'), $selected.attr('data-message-title')); | |
| layer_close(); | |
| }); | |
| })(); | |
| //--> | |
| </script> | |
| </div> | |
변경 전layer_sms_template_preview.php
변경 후layer_sms_template_preview.php
| <link rel="stylesheet" href="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'css/layer-sms-template.css') ?>"/> | |
| <div class="sms-template-preview"> | |
| <div class="preview-message"> | |
| <?php if (($template['messageTitle'] ?? '') !== ''): ?> | |
| <div class="preview-message-title"><?= $template['messageTitle'] ?></div> | |
| <?php endif; ?> | |
| <?= nl2br($template['contents']) ?> | |
| </div> | |
| </div> | |
| <?php if ($template['isUsedByAutoSend'] && !empty($latestApproved) && $template['approvalStatus'] !== 'APPROVED'): ?> | |
| <div class="notice-info mgt10">검수가 완료될 때까지 자동 알림은 이전 승인본으로 발송됩니다.</div> | |
| <div class="sms-template-preview mgt10"> | |
| <div class="preview-message"><?= nl2br($latestApproved['templateContent']) ?></div> | |
| </div> | |
| <?php endif; ?> | |
| <div class="text-center"> | |
| <button type="button" class="btn btn-lg btn-black" onclick="layer_close()">닫기</button> | |
| </div> | |
변경 전sms_auto.php
| <!-- //@formatter:off --> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script> | |
| <input type="hidden" id="kakaoUse" name="kakaoUse" value="<?php echo $kakaoSetting['useFlag']; ?>"/> | |
| <input type="hidden" id="kakaoOrderUse" name="kakaoOrderUse" value="<?php echo $kakaoSetting['orderUseFlag']; ?>"/> | |
| <input type="hidden" id="kakaoMemberUse" name="kakaoMemberUse" value="<?php echo $kakaoSetting['memberUseFlag']; ?>"/> | |
| <h3><?php echo end($naviMenu->location); ?> | |
| </h3> | |
| <div class="btn-group"> | |
| <input type="submit" value="설정 저장" class="btn btn-red"/> | |
| </div> | |
| </div> | |
| </label> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][memberMode]" value="<?php echo $typeData['memberMode']; ?>"/> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][memberSno]" value="<?php echo $typeData['memberSno']; ?>"/> | |
| <div class="js-sms-auto-subject-wrap mgb5"> | |
| <input type="text" name="<?= $sKey ?>[<?= $typeData['code'] ?>][memberMessageTitle]" class="form-control js-sms-auto-subject" placeholder="제목 입력" value="<?= gd_isset($typeData['memberMessageTitle']) ?>"/> | |
| </div> | |
| <textarea name="<?= $sKey ?>[<?= $typeData['code'] ?>][memberContents]" rows="4" class="form-control js-sms-auto-contents"><?= $typeData['memberContents'] ?></textarea> | |
| <?php } else { ?> | |
| <div class="text-center text-blue">운영자 전용</div> | |
| <?php } ?> | |
| </label> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][adminMode]" value="<?php echo $typeData['adminMode']; ?>"/> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][adminSno]" value="<?php echo $typeData['adminSno']; ?>"/> | |
| <div class="js-sms-auto-subject-wrap mgb5"> | |
| <input type="text" name="<?= $sKey ?>[<?= $typeData['code'] ?>][adminMessageTitle]" class="form-control js-sms-auto-subject" placeholder="제목 입력" value="<?= gd_isset($typeData['adminMessageTitle']) ?>"/> | |
| </div> | |
| <textarea name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][adminContents]" rows="4" | |
| class="form-control js-sms-auto-contents <?php if ($sKey == 'admin') { ?> width100p <?php } ?>"><?= $typeData['adminContents'] ?></textarea> | |
| <?php } else { ?> | |
| <div class="text-center text-orange-red">회원 전용</div> | |
| <?php } ?> | |
| </label> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][providerMode]" value="<?php echo $typeData['providerMode']; ?>"/> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][providerSno]" value="<?php echo $typeData['providerSno']; ?>"/> | |
| <div class="js-sms-auto-subject-wrap mgb5"> | |
| <input type="text" name="<?= $sKey ?>[<?= $typeData['code'] ?>][providerMessageTitle]" class="form-control js-sms-auto-subject" placeholder="제목 입력" value="<?= gd_isset($typeData['providerMessageTitle']) ?>"/> | |
| </div> | |
| <textarea name="<?= $sKey ?>[<?= $typeData['code'] ?>][providerContents]" rows="4" class="form-control js-sms-auto-contents"><?= $typeData['providerContents'] ?></textarea> | |
| <?php } else { ?> | |
| <?php if ($sKey == 'admin') { ?> | |
| <div class="text-center text-orange-red">본사 전용</div> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][providerSend]" value="<?php echo $typeData['providerSend'] === 'y' ? 'y' : 'n'; ?>"/> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][providerSno]" value="<?php echo $typeData['providerSno']; ?>"/> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][providerMode]" value="<?php echo $typeData['providerMode']; ?>"/> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][providerContents]" value="<?php echo $typeData['providerContents']; ?>"/> | |
| <input type="hidden" name="<?= $sKey ?>[<?= $typeData['code'] ?>][providerMessageTitle]" value="<?= gd_isset($typeData['providerMessageTitle']) ?>"/> | |
| <?php } ?> | |
| </tr> | |
| <?php | |
| <script type="text/javascript"> | |
| <!-- | |
| $(document).ready(function () { | |
| // 제목 바이트 계산 (한글 등 멀티바이트 2byte, ASCII 1byte, 개행 제외) | |
| function smsAutoStringToByte(str) { | |
| var length = 0; | |
| for (var i = 0; i < str.length; i++) { | |
| if (escape(str.charAt(i)).length >= 4) length += 2; | |
| else if (escape(str.charAt(i)) != "%0D") length++; | |
| } | |
| return length; | |
| } | |
| // 이모티콘 제거 (서로게이트 페어 + 변형선택자, 기획: 제목·본문 입력 불가) | |
| function smsAutoStripEmoji(str) { | |
| if (!str) return str; | |
| return str.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, '').replace(/[\uFE00-\uFE0F\u200D\u20E3]/g, ''); | |
| } | |
| // 자동발송 제목 영역은 항상 활성 노출(C3). 본문은 이모티콘만 자동 제거. | |
| $('.js-sms-auto-contents').on('keyup change', function () { | |
| var $ta = $(this); | |
| var stripped = smsAutoStripEmoji($ta.val()); | |
| if (stripped !== $ta.val()) { $ta.val(stripped); } | |
| }); | |
| $('.js-sms-auto-subject').on('keyup keydown', function () { | |
| var input = $(this); | |
| var text = smsAutoStripEmoji(input.val()); | |
| while (smsAutoStringToByte(text) > <?= $lmsSubjectLimit; ?>) { text = text.substring(0, text.length - 1); } | |
| input.val(text); | |
| }); | |
| $('.btn-change-password').click(function () { | |
| $.get('../share/layer_sms_password.php?mode=change', function () { | |
변경 후sms_auto.php
| <!-- //@formatter:off --> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script> | |
| <script src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-emoji.js') ?>"></script> | |
| <input type="hidden" id="kakaoUse" name="kakaoUse" value="<?php echo $kakaoSetting['useFlag']; ?>"/> | |
| <input type="hidden" id="kakaoOrderUse" name="kakaoOrderUse" value="<?php echo $kakaoSetting['orderUseFlag']; ?>"/> | |
| <input type="hidden" id="kakaoMemberUse" name="kakaoMemberUse" value="<?php echo $kakaoSetting['memberUseFlag']; ?>"/> | |
| <h3><?php echo end($naviMenu->location); ?> | |
| </h3> | |
| <div class="btn-group"> | |
| <button type="button" class="btn btn-white" onclick="location.href='sms_template.php';">SMS 템플릿 관리</button> | |
| <input type="submit" value="설정 저장" class="btn btn-red"/> | |
| </div> | |
| </div> | |
| </label> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][memberMode]" value="<?php echo $typeData['memberMode']; ?>"/> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][memberSno]" value="<?php echo $typeData['memberSno']; ?>"/> | |
| <input type="hidden" name="<?= $sKey ?>[<?= $typeData['code'] ?>][memberReferenceSmsContentNo]" value="<?= $typeData['memberReferenceSmsContentNo'] ?>"/> | |
| <?php $smsAutoTemplateSelection = $smsAutoTemplateSelections[$sKey][$typeData['code']]['member'] ?? []; ?> | |
| <div><textarea rows="1" class="form-control js-sms-auto-title mgb5" readonly><?= htmlspecialchars($smsAutoTemplateSelection['selectedLmsTitle'] ?? '', ENT_QUOTES) ?></textarea></div> | |
| <textarea rows="4" class="form-control js-sms-auto-contents mgb5" readonly><?= htmlspecialchars($smsAutoTemplateSelection['selectedContents'] ?? '', ENT_QUOTES) ?></textarea> | |
| <input type="hidden" class="js-sms-auto-template-cleared" name="<?= $sKey ?>[<?= $typeData['code'] ?>][memberTemplateCleared]" value="n"/> | |
| <select name="<?= $sKey ?>[<?= $typeData['code'] ?>][memberSourceTemplateSno]" class="form-control js-sms-auto-template"> | |
| <option value="">템플릿 선택</option> | |
| <?php foreach (['고도몰 템플릿' => 'godomallTemplates', '사용자 템플릿' => 'userTemplates'] as $smsAutoTemplateGroupLabel => $smsAutoTemplateGroupKey): ?> | |
| <?php if (($smsAutoTemplateSelection[$smsAutoTemplateGroupKey] ?? []) !== []): ?> | |
| <optgroup label="<?= $smsAutoTemplateGroupLabel ?>"> | |
| <?php foreach ($smsAutoTemplateSelection[$smsAutoTemplateGroupKey] as $smsAutoTemplate): ?> | |
| <option value="<?= $smsAutoTemplate['sno'] ?>" <?= $smsAutoTemplate['sno'] === $smsAutoTemplateSelection['selectedSno'] ? 'selected' : '' ?>><?= htmlspecialchars($smsAutoTemplate['name'], ENT_QUOTES) ?></option> | |
| <?php endforeach; ?> | |
| </optgroup> | |
| <?php endif; ?> | |
| <?php endforeach; ?> | |
| </select> | |
| <?php } else { ?> | |
| <div class="text-center text-blue">운영자 전용</div> | |
| <?php } ?> | |
| </label> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][adminMode]" value="<?php echo $typeData['adminMode']; ?>"/> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][adminSno]" value="<?php echo $typeData['adminSno']; ?>"/> | |
| <input type="hidden" name="<?= $sKey ?>[<?= $typeData['code'] ?>][adminReferenceSmsContentNo]" value="<?= $typeData['adminReferenceSmsContentNo'] ?>"/> | |
| <?php $smsAutoTemplateSelection = $smsAutoTemplateSelections[$sKey][$typeData['code']]['admin'] ?? []; ?> | |
| <div><textarea rows="1" class="form-control js-sms-auto-title mgb5" readonly><?= htmlspecialchars($smsAutoTemplateSelection['selectedLmsTitle'] ?? '', ENT_QUOTES) ?></textarea></div> | |
| <textarea rows="4" class="form-control js-sms-auto-contents mgb5" readonly><?= htmlspecialchars($smsAutoTemplateSelection['selectedContents'] ?? '', ENT_QUOTES) ?></textarea> | |
| <input type="hidden" class="js-sms-auto-template-cleared" name="<?= $sKey ?>[<?= $typeData['code'] ?>][adminTemplateCleared]" value="n"/> | |
| <select name="<?= $sKey ?>[<?= $typeData['code'] ?>][adminSourceTemplateSno]" class="form-control js-sms-auto-template"> | |
| <option value="">템플릿 선택</option> | |
| <?php foreach (['고도몰 템플릿' => 'godomallTemplates', '사용자 템플릿' => 'userTemplates'] as $smsAutoTemplateGroupLabel => $smsAutoTemplateGroupKey): ?> | |
| <?php if (($smsAutoTemplateSelection[$smsAutoTemplateGroupKey] ?? []) !== []): ?> | |
| <optgroup label="<?= $smsAutoTemplateGroupLabel ?>"> | |
| <?php foreach ($smsAutoTemplateSelection[$smsAutoTemplateGroupKey] as $smsAutoTemplate): ?> | |
| <option value="<?= $smsAutoTemplate['sno'] ?>" <?= $smsAutoTemplate['sno'] === $smsAutoTemplateSelection['selectedSno'] ? 'selected' : '' ?>><?= htmlspecialchars($smsAutoTemplate['name'], ENT_QUOTES) ?></option> | |
| <?php endforeach; ?> | |
| </optgroup> | |
| <?php endif; ?> | |
| <?php endforeach; ?> | |
| </select> | |
| <?php } else { ?> | |
| <div class="text-center text-orange-red">회원 전용</div> | |
| <?php } ?> | |
| </label> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][providerMode]" value="<?php echo $typeData['providerMode']; ?>"/> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][providerSno]" value="<?php echo $typeData['providerSno']; ?>"/> | |
| <input type="hidden" name="<?= $sKey ?>[<?= $typeData['code'] ?>][providerReferenceSmsContentNo]" value="<?= $typeData['providerReferenceSmsContentNo'] ?>"/> | |
| <?php $smsAutoTemplateSelection = $smsAutoTemplateSelections[$sKey][$typeData['code']]['provider'] ?? []; ?> | |
| <div><textarea rows="1" class="form-control js-sms-auto-title mgb5" readonly><?= htmlspecialchars($smsAutoTemplateSelection['selectedLmsTitle'] ?? '', ENT_QUOTES) ?></textarea></div> | |
| <textarea rows="4" class="form-control js-sms-auto-contents mgb5" readonly><?= htmlspecialchars($smsAutoTemplateSelection['selectedContents'] ?? '', ENT_QUOTES) ?></textarea> | |
| <input type="hidden" class="js-sms-auto-template-cleared" name="<?= $sKey ?>[<?= $typeData['code'] ?>][providerTemplateCleared]" value="n"/> | |
| <select name="<?= $sKey ?>[<?= $typeData['code'] ?>][providerSourceTemplateSno]" class="form-control js-sms-auto-template"> | |
| <option value="">템플릿 선택</option> | |
| <?php foreach (['고도몰 템플릿' => 'godomallTemplates', '사용자 템플릿' => 'userTemplates'] as $smsAutoTemplateGroupLabel => $smsAutoTemplateGroupKey): ?> | |
| <?php if (($smsAutoTemplateSelection[$smsAutoTemplateGroupKey] ?? []) !== []): ?> | |
| <optgroup label="<?= $smsAutoTemplateGroupLabel ?>"> | |
| <?php foreach ($smsAutoTemplateSelection[$smsAutoTemplateGroupKey] as $smsAutoTemplate): ?> | |
| <option value="<?= $smsAutoTemplate['sno'] ?>" <?= $smsAutoTemplate['sno'] === $smsAutoTemplateSelection['selectedSno'] ? 'selected' : '' ?>><?= htmlspecialchars($smsAutoTemplate['name'], ENT_QUOTES) ?></option> | |
| <?php endforeach; ?> | |
| </optgroup> | |
| <?php endif; ?> | |
| <?php endforeach; ?> | |
| </select> | |
| <?php } else { ?> | |
| <?php if ($sKey == 'admin') { ?> | |
| <div class="text-center text-orange-red">본사 전용</div> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][providerSend]" value="<?php echo $typeData['providerSend'] === 'y' ? 'y' : 'n'; ?>"/> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][providerSno]" value="<?php echo $typeData['providerSno']; ?>"/> | |
| <input type="hidden" name="<?php echo $sKey; ?>[<?php echo $typeData['code']; ?>][providerMode]" value="<?php echo $typeData['providerMode']; ?>"/> | |
| <input type="hidden" name="<?= $sKey ?>[<?= $typeData['code'] ?>][providerReferenceSmsContentNo]" value="<?= $typeData['providerReferenceSmsContentNo'] ?? '' ?>"/> | |
| <input type="hidden" name="<?= $sKey ?>[<?= $typeData['code'] ?>][providerSourceTemplateSno]" value="<?= $smsAutoTemplateSelections[$sKey][$typeData['code']]['provider']['selectedSno'] ?? '' ?>"/> | |
| <?php } ?> | |
| </tr> | |
| <?php | |
| <script type="text/javascript"> | |
| <!-- | |
| $(document).ready(function () { | |
| smsEmoji.bind('#frmSmsAuto textarea'); | |
| var smsAutoTemplateContents = <?= json_encode($smsAutoTemplateContents, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_FORCE_OBJECT) ?>; | |
| var smsAutoTemplateLmsTitles = <?= json_encode($smsAutoTemplateLmsTitles ?? [], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_FORCE_OBJECT) ?>; | |
| $('#frmSmsAuto').on('change', '.js-sms-auto-template', function () { | |
| $(this).parent().find('.js-sms-auto-title').val(smsAutoTemplateLmsTitles[$(this).val()] || ''); | |
| $(this).siblings('.js-sms-auto-contents').val(smsAutoTemplateContents[$(this).val()] || ''); | |
| $(this).siblings('.js-sms-auto-template-cleared').val($(this).val() ? 'n' : 'y'); | |
| }); | |
| $('.btn-change-password').click(function () { | |
| $.get('../share/layer_sms_password.php?mode=change', function () { | |
변경 전_sms_replace_code_table.php
변경 후_sms_replace_code_table.php
| <?php | |
| /* | |
| * Copyright (C) 2026 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| ?> | |
| <div class="table-scroll"> | |
| <table class="table table-bordered table-rows mgb0 js-table-replace-code"> | |
| <colgroup> | |
| <col class="width-sm"> | |
| <col class="width-2xl"> | |
| <col class="width-3xs"> | |
| </colgroup> | |
| <thead> | |
| <tr> | |
| <th>치환코드</th> | |
| <th>설명</th> | |
| <th>삽입</th> | |
| </tr> | |
| </thead> | |
| <?php foreach ($replaceCodeTableGroups as $group => $codes): ?> | |
| <tbody class="replace-code-area display-none" data-type="<?= $group ?>"> | |
| <?php foreach ($codes as $code => $description): ?> | |
| <tr> <td class="center"><?= $code ?></td> <td><?= $description ?></td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <?php endforeach; ?> | |
| </tbody> | |
| <?php endforeach; ?> | |
| </table> | |
| </div> | |
변경 전sms_send.php
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script> | |
| <script type="text/javascript"> | |
| <!-- | |
| /** | |
| $('input[name=receiverCnt]').val(receiverCount * pointCnt); | |
| $('input[name=agreeCnt]').val(agreeCount * pointCnt); | |
| $('input[name=rejectCnt]').val(rejectCount * pointCnt); | |
| } | |
| $(document).ready(function () { | |
| gd_sms_send.set_form($('#formSendSms')); | |
| gd_sms_send.set_pagination($('.js-pagination')); | |
| gd_sms_send.set_target_area($('.sms-target-area')); | |
| gd_sms_send.set_search_word($('input[name="searchWord"]')); | |
| gd_sms_send.set_contents_list($('.sms-contents-area')); | |
| gd_sms_send.set_sms_contents_search_area($('.js-sms-contents-search-area')); | |
| gd_sms_send.set_sms_receiver_list($('#divSmsList')); | |
| gd_sms_send.set_submit_excel('#formExcel'); | |
| gd_sms_send.set_on_click_select_target('#btnSearchMember, #linkSearchMember'); | |
| gd_sms_send.set_on_click_direct_list_item('.js-direct-list .list-group-item'); | |
| gd_sms_send.set_on_click_delete_cell_phone_select('.js-btn-delete-selected'); | |
| gd_sms_send.set_on_click_delete_cell_phone_all('.js-btn-delete-all'); | |
| gd_sms_send.set_on_click_pagination('.page_navi_number'); | |
| gd_sms_send.set_on_click_check_contents_view_all('.js-btn-select-sms-all'); | |
| gd_sms_send.set_on_click_delete_sms_selected('.js-btn-delete-sms-selected'); | |
| gd_sms_send.set_on_click_save_contents_by_dialog('.js-btn-dialog-save'); | |
| gd_sms_send.set_click_sms080_contents(':checkbox[name="sms080Reject"]'); | |
| gd_sms_send.set_click_save_contents('.js-btn-save-contents'); | |
| gd_sms_send.set_click_more_contents('.js-btn-sms-contents'); | |
| gd_sms_send.set_click_receiver_type(':radio[name=receiverType]'); | |
| gd_sms_send.set_click_replace_code_insert('.replace-code-area .js-btn-insert'); | |
| gd_sms_send.set_click_excel_upload('.js-btn-excel-upload'); | |
| gd_sms_send.set_click_contents_search('.js-btn-search-contents'); | |
| gd_sms_send.set_change_replace_code_group('select[name=replaceCodeGroup]'); | |
| gd_sms_send.set_change_focus_contents('textarea[name="smsContents"]'); | |
| gd_sms_send.set_change_auto_code_selector($('select[name="smsAutoCode"]')); | |
| // 대량발송 안내문구 초기화 | |
| $('input[name="receiverType"]').click(function () { | |
| var $formSendSms = gd_sms_send.get_form(); | |
| // 폼 체크 | |
| $formSendSms.validate({ | |
| dialog: false, | |
| <input type="hidden" name="receiverCnt" value="" /> | |
| <input type="hidden" name="agreeCnt" value="" /> | |
| <input type="hidden" name="rejectCnt" value="<?=$countSmsFlN?>" /> | |
| <input type="hidden" name="sendFl" value="sms" /> | |
| <input type="hidden" name="rejectSend" value="n" /> | |
| <input type="hidden" name="receiverList" value="" /> | |
| </label> | |
| </div> | |
| <div class="col-xs-9 display-none replace_code_area"> | |
| <div class="table-scroll"> | |
| <table class="table table-bordered table-rows mgb0 js-table-replace-code"> | |
| <colgroup> | |
| <col class="width-sm"> | |
| <col class="width-2xl"> | |
| <col class="width-3xs"> | |
| </colgroup> | |
| <thead> | |
| <tr> | |
| <th>치환코드</th> | |
| <th>설명</th> | |
| <th>삽입</th> | |
| </tr> | |
| </thead> | |
| <tbody class="replace-code-area display-none" data-type="goods"> | |
| <tr> <td class="center">[{rc_mallNm}]</td> <td>쇼핑몰 명, 상점명</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{restockName}</td> <td>(재입고 알림) 신청자명</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{restockGoodsNm}</td> <td>(재입고 알림) 상품명</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{restockOptionName}</td> <td>(재입고 알림) 상품옵션</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{restockGoodsUrl}</td> <td>(재입고 알림) 상품 URL</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| </tbody> | |
| <tbody class="replace-code-area display-none" data-type="order"> | |
| <tr> <td class="center">[{rc_mallNm}]</td> <td>쇼핑몰 명, 상점명</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{orderName}</td> <td>주문자 이름</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{orderNo}</td> <td>주문번호</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{settlePrice}</td> <td>총 주문 금액</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{cancelPrice}</td> <td>취소금액</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{gbRefundPrice}</td> <td>실 환불금액</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{depositNm}</td> <td>입금자명</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{bankAccount}</td> <td>입금계좌번호</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{bankInfo}</td> <td>은행명/계좌번호/예금주</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{orderDate}</td> <td>주문일</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{expirationDate}</td> <td>입금만료일</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| </tbody> | |
| <tbody class="replace-code-area display-none" data-type="member"> | |
| <tr> <td class="center">[{rc_mallNm}]</td> <td>쇼핑몰 명, 상점명</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{memId}</td> <td>회원 아이디</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{memNm}</td> <td>회원명</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{sleepScheduleDt}</td> <td>휴면회원전환예정일</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{rc_scheduleDt}</td> <td>일반회원전환일</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{smsAgreementDt}</td> <td>SMS 수신동의일</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{mailAgreementDt}</td> <td>메일 수신동의일</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{groupNm}</td> <td>회원등급</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{mileage}</td> <td>보유한 마일리지</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{deposit}</td> <td>보유한 예치금</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| </tbody> | |
| <tbody class="replace-code-area display-none" data-type="promotion"> | |
| <tr> <td class="center">[{rc_mallNm}]</td> <td>쇼핑몰 명, 상점명</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{memNm}</td> <td>회원명 (없는 경우 공백으로 치환)</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{eventNm}</td> <td>(기획전 홍보) 기획전명</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{eventDt}</td> <td>(기획전 홍보) 기획전 기간</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{eventUrl}</td> <td>(기획전 홍보) 기획전 url</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| </tbody> | |
| <tbody class="replace-code-area display-none" data-type="board"> | |
| <tr> <td class="center">[{rc_mallNm}]</td> <td>쇼핑몰 명, 상점명</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| <tr> <td class="center">{memNm}</td> <td>회원명</td> <td class="center"> <button class="btn btn-sm btn-white js-btn-insert" type="button">삽입</button> </td> </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="row pdt10"> | |
| <div class="notice-info">URL 삽입 시 [단축 URL] 앱을 이용하시면 짧고 간단하게 URL을 사용하실 수 있습니다. <a href="https://apps.godo.co.kr/apps/1519" class="btn-link" target="_blank">바로가기</a></div> | |
| <div class="notice-info">치환코드 사용 시 LMS로 발송될 수 있으며 일부 문자가 포인트 부족으로 발송되지 않을 수 있습니다.<br/>- 포인트 충전 후 <a href="../member/sms_log.php" class="btn-link" target="_blank">SMS 발송 내역 보기</a>에서 재발송하실 수 있습니다.</div> | |
| <div class="notice-info">본문 내용이 LMS인 경우에만 제목 입력이 가능하며, 제목 영역에는 직접 (광고) 문구를 입력하지 않도록 하고 광고 삽입이 필요한 경우 광고성 문구 추가 기능을 사용해야 합니다.</div> | |
| <div class="notice-danger sms-large-caution">발송내용에 이모지 또는 사용 불가능한 특수문자, 맞춤법이 바르지 않은 문자가 포함되어있으면 일부 내용은 삭제되고 발송되오니 주의하시기 바랍니다.<br/>대량 SMS(LMS) 발송 시 이모지를 사용하지 않는 것과 맞춤법에 어긋나는 문자가 입력 되지는 않았는지 확인하시는 것을 권장 드리며,<br/> 발송 내용에 사용가능한 특수문자는 매뉴얼을 참고하시기 바랍니다.</div> | |
| </div> | |
| </div> | |
| <div class="row pdt10"> | |
| <div class="col-xs-12"> | |
| <button type="button" class="btn btn-white js-btn-sms-contents">발송 내용 관리<span class="glyphicon glyphicon-triangle-bottom" aria-hidden="true"></span></button> | |
| <button type="button" class="btn btn-icon-plus-red-box js-btn-save-contents">현재 내용 저장</button> | |
| </div> | |
| </div> | |
| <div class="row pdt10 js-sms-contents-search-area display-none"> | |
| <div class="col-xs-12"> | |
| <div class="row"> | |
| <div class="col-xs-6 pdr0"> | |
| <label> | |
| <?= gd_select_box('smsAutoCode', 'smsAutoCode', $smsContentsGroupCode, null, $smsAutoCode, '전체') ?> | |
| </label> | |
| <input type="text" class="form-control width-xl" placeholder="제목이나 내용을 입력하여 주세요." name="searchWord"> | |
| <button type="button" class="btn btn-sm btn-gray js-btn-search-contents">검색</button> | |
| </div> | |
| <div class="col-xs-6"> | |
| <div class="pull-right"> | |
| <button type="button" class="btn btn-white mgr5 js-btn-select-sms-all">전체선택</button> | |
| <button type="button" class="btn btn-white js-btn-delete-sms-selected">선택삭제</button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="row pdt10"> | |
| <div class="col-xs-12"> | |
| <div class="sms-contents-area row"></div> | |
| </div> | |
| </div> | |
| <div class="row"> | |
| <div class="col-xs-12"> | |
| <div class="center js-pagination"></div> | |
| </div> | |
| </div> | |
| <div class="row"> | |
| <div class="col-xs-12"> | |
| <div class="notice-danger">치환코드는 지정된 페이지에서만 사용하실 수 있습니다.</div> | |
| <div class="notice-info">- 예) 주문 관련 치환코드는 주문리스트에서 SMS 발송 시에만 사용하실 수 있습니다.</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </td> | |
| <button type="button" class="btn btn-icon-delete" data-toggle="delete" data-target="#info_crm_group_<%= targetNo %>">삭제</button> | |
| </div> | |
| </script> | |
| <script type="text/html" id="templateSmsContents"> | |
| <div class="col-xs-4"> | |
| <div class="sms-contents-view pd10"> | |
| <%= checkbox %> <%= subject %> | |
| <button type="button" class="btn btn-sm btn-white pull-right js-btn-save" data-text="저장">수정</button> | |
| <button type="button" class="btn btn-sm btn-white pull-right js-btn-insert mgr5">삽입</button> | |
| <input type="text" name="messageTitle" class="form-control width100p mgt5 js-sms-contents-subject" placeholder="제목을 입력해주세요." value="<%= messageTitle %>" disabled="disabled"/> | |
| <label class="width100p pdt5"> | |
| <textarea rows="13" class="form-control width100p" name="contents"><%= contents %></textarea> | |
| </label> | |
| </div> | |
| </div> | |
| </script> | |
| <script type="text/html" id="templateEmptySmsContents"> | |
| <div class="col-xs-12 center mgl30t"> | |
| <span>검색된 정보가 없습니다.</span> | |
| </div> | |
| </script> | |
| <script type="text/template" id="templatePagination"> | |
| <nav> | |
| <ul class="pagination pagination-sm"> | |
| <% for (var i = page.start; i <= page.end; i++) { %> <% if (i === (page.now *1)) { %> | |
| <li class="active"> | |
| <span><%- i %></span> | |
| </li> | |
| <% } else { %> | |
| <li><a href="#" class="page_navi_number"><%- i %></a></li> | |
| <% } %> <% } %> | |
| </ul> | |
| </nav> | |
| </script> | |
| <script type="text/html" id="templateSaveContentsDialog"> | |
| <div class="layer_wrap"> | |
| <div class="notice-info">작성한 내용을 저장할 그룹과 제목을 선택하여 주시기 바랍니다.</div> | |
| <div class="table-cols form-inline top-border-clear pdt10"> | |
| <?= gd_select_box('dialogSmsAutoCode', 'dialogSmsAutoCode', $smsContentsGroupCode, null, null, '그룹 선택') ?> | |
| <input type="text" class="form-control width-xl js-maxlength" placeholder="제목을 입력하여 주세요." name="dialogSubject" maxlength="10"> | |
| </div> | |
| <div class="pdt30 center"> | |
| <button type="button" class="btn btn-red btn-lg js-btn-dialog-save">저장</button> | |
변경 후sms_send.php
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script> | |
| <script type="text/javascript" src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-send-gate.js') ?>"></script> | |
| <script type="text/javascript"> | |
| $(function () { | |
| SmsSendGate.applyRestrict('<?= gd_isset($smsSendRestrictType, '') ?>', { | |
| tooltip: '<?= gd_isset($smsSendRestrictTooltip, '') ?>', | |
| selectors: ['#formSendSms input[type="submit"]', '#formSendSms textarea[name="smsContents"]'] | |
| }); | |
| }); | |
| </script> | |
| <script type="text/javascript"> | |
| <!-- | |
| /** | |
| $('input[name=receiverCnt]').val(receiverCount * pointCnt); | |
| $('input[name=agreeCnt]').val(agreeCount * pointCnt); | |
| $('input[name=rejectCnt]').val(rejectCount * pointCnt); | |
| $('input[name=sendTargetCnt]').val(receiverCount); | |
| } | |
| var LAYER_FAIL_MESSAGE = '일시적인 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.'; | |
| // 진입 제한 문구도 <br> 포함이라 자식 엘리먼트 유무로 구분 불가 — layout_layer.php 의 #layer-wrap 유무로 판별 | |
| function isLayerHtml(html) { | |
| var $nodes = $($.parseHTML($.trim(String(html)), false)); | |
| return $nodes.filter('#layer-wrap').length > 0 || $nodes.find('#layer-wrap').length > 0; | |
| } | |
| var gd_sms_template = { | |
| open: function () { | |
| $.post('../member/layer_sms_template_load.php', {}, function (html) { | |
| if (!isLayerHtml(html)) { | |
| dialog_alert(LAYER_FAIL_MESSAGE, '정보'); | |
| return; | |
| } | |
| layer_popup(html, '템플릿 불러오기', 'wide'); | |
| }).fail(function (xhr) { | |
| var response = xhr.responseJSON; | |
| dialog_alert(xhr.status === 403 && response && response.message ? response.message : LAYER_FAIL_MESSAGE, '정보'); | |
| }); | |
| }, | |
| apply: function (contents, messageTitle) { | |
| var $contents = $('textarea[name="smsContents"]'); | |
| var element = $contents.val(contents).get(0); | |
| element.selectionStart = element.selectionEnd = contents.length; | |
| $contents.trigger('focusout').focus().trigger('keyup'); | |
| if ($('#smsSubject').length) { | |
| $('#smsSubject').val(messageTitle || ''); | |
| setSubjectLength(); | |
| } | |
| } | |
| }; | |
| $(document).ready(function () { | |
| gd_sms_send.set_form($('#formSendSms')); | |
| // 공용 sms.js 의 레거시 발송 내용 관리 요소 검증·저장 후 목록 재조회를 빈 요소로 비활성화 | |
| gd_sms_send.set_pagination($()); | |
| gd_sms_send.set_search_word($()); | |
| gd_sms_send.set_contents_list($()); | |
| gd_sms_send.set_sms_contents_search_area($('<div class="display-none"></div>')); | |
| gd_sms_send.set_change_auto_code_selector($()); | |
| gd_sms_send.set_click_more_contents($()); | |
| gd_sms_send.set_target_area($('.sms-target-area')); | |
| gd_sms_send.set_sms_receiver_list($('#divSmsList')); | |
| gd_sms_send.set_submit_excel('#formExcel'); | |
| gd_sms_send.set_on_click_select_target('#btnSearchMember, #linkSearchMember'); | |
| gd_sms_send.set_on_click_direct_list_item('.js-direct-list .list-group-item'); | |
| gd_sms_send.set_on_click_delete_cell_phone_select('.js-btn-delete-selected'); | |
| gd_sms_send.set_on_click_delete_cell_phone_all('.js-btn-delete-all'); | |
| gd_sms_send.set_on_click_save_contents_by_dialog('.js-btn-dialog-save'); | |
| gd_sms_send.set_click_sms080_contents(':checkbox[name="sms080Reject"]'); | |
| gd_sms_send.set_click_save_contents('.js-btn-save-contents'); | |
| $('.js-btn-load-template').click(gd_sms_template.open); | |
| gd_sms_send.set_click_receiver_type(':radio[name=receiverType]'); | |
| gd_sms_send.set_click_replace_code_insert('.replace-code-area .js-btn-insert'); | |
| gd_sms_send.set_click_excel_upload('.js-btn-excel-upload'); | |
| gd_sms_send.set_change_replace_code_group('select[name=replaceCodeGroup]'); | |
| gd_sms_send.set_change_focus_contents('textarea[name="smsContents"]'); | |
| $(document).on('shown.bs.modal', '.modal', function () { | |
| var $modal = $(this); | |
| if ($modal.find('select[name="dialogSmsAutoCode"]').length) { | |
| $modal.find('.bootstrap-dialog-title').text('템플릿으로 저장'); | |
| } | |
| }); | |
| // 대량발송 안내문구 초기화 | |
| $('input[name="receiverType"]').click(function () { | |
| var $formSendSms = gd_sms_send.get_form(); | |
| // 점유인증이 입력값 유효성보다 앞이라 submitHandler 로 옮기면 안 된다 | |
| $formSendSms.on('click', 'input[type="submit"]', function (event) { | |
| event.preventDefault(); | |
| // 발송 건수는 유효성 검증 단계에서 채워지는데 게이트가 그보다 앞이라 여기서 먼저 세운다 | |
| setReceiverCount(); | |
| SmsSendGate.ensure({ | |
| targetCount: parseInt($('input[name=sendTargetCnt]').val()) || 0 | |
| }).then(function () { | |
| $formSendSms.submit(); | |
| }).catch(function () {}); | |
| }); | |
| // 폼 체크 | |
| $formSendSms.validate({ | |
| dialog: false, | |
| <input type="hidden" name="receiverCnt" value="" /> | |
| <input type="hidden" name="agreeCnt" value="" /> | |
| <input type="hidden" name="rejectCnt" value="<?=$countSmsFlN?>" /> | |
| <input type="hidden" name="sendTargetCnt" value="" /> | |
| <input type="hidden" name="sendFl" value="sms" /> | |
| <input type="hidden" name="rejectSend" value="n" /> | |
| <input type="hidden" name="receiverList" value="" /> | |
| </label> | |
| </div> | |
| <div class="col-xs-9 display-none replace_code_area"> | |
| <?php include('_sms_replace_code_table.php'); ?> | |
| </div> | |
| </div> | |
| <div class="row pdt10"> | |
| <div class="notice-info">URL 삽입 시 [단축 URL] 앱을 이용하시면 짧고 간단하게 URL을 사용하실 수 있습니다. <a href="https://apps.godo.co.kr/apps/1519" class="btn-link" target="_blank">바로가기</a></div> | |
| <div class="notice-info">치환코드 사용 시 LMS로 발송될 수 있으며 일부 문자가 포인트 부족으로 발송되지 않을 수 있습니다.<br/>- 포인트 충전 후 <a href="../member/sms_log.php" class="btn-link" target="_blank">SMS 발송 내역 보기</a>에서 재발송하실 수 있습니다.</div> | |
| <div class="notice-info">본문 내용이 LMS인 경우에만 제목 입력이 가능하며, 제목 영역에는 직접 (광고) 문구를 입력하지 않도록 하고 광고 삽입이 필요한 경우 광고성 문구 추가 기능을 사용해야 합니다.</div> | |
| <div class="notice-info">템플릿 작성 및 수정은 템플릿 관리에서 가능합니다. <a href="./sms_template.php" class="btn-link" target="_blank">바로가기</a></div> | |
| <div class="notice-info">SMS/LMS 템플릿은 승인 상태에서만 발송이 가능합니다. (검수중·반려 상태의 템플릿은 발송이 제한됩니다)</div> | |
| <div class="notice-danger sms-large-caution">발송내용에 이모지 또는 사용 불가능한 특수문자, 맞춤법이 바르지 않은 문자가 포함되어있으면 일부 내용은 삭제되고 발송되오니 주의하시기 바랍니다.<br/>대량 SMS(LMS) 발송 시 이모지를 사용하지 않는 것과 맞춤법에 어긋나는 문자가 입력 되지는 않았는지 확인하시는 것을 권장 드리며,<br/> 발송 내용에 사용가능한 특수문자는 매뉴얼을 참고하시기 바랍니다.</div> | |
| </div> | |
| </div> | |
| <div class="row pdt10"> | |
| <div class="col-xs-12"> | |
| <button type="button" class="btn btn-white js-btn-load-template">템플릿 불러오기</button> | |
| <button type="button" class="btn btn-icon-plus-red-box js-btn-save-contents">템플릿으로 저장하기</button> | |
| </div> | |
| </div> | |
| </td> | |
| <button type="button" class="btn btn-icon-delete" data-toggle="delete" data-target="#info_crm_group_<%= targetNo %>">삭제</button> | |
| </div> | |
| </script> | |
| <script type="text/html" id="templateSaveContentsDialog"> | |
| <div class="layer_wrap"> | |
| <div class="notice-info">작성한 내용을 저장할 카테고리와 템플릿명을 입력하여 주시기 바랍니다.</div> | |
| <div class="notice-info">저장한 템플릿은 검수중 상태로 등록되며, 승인 후 발송 가능합니다.</div> | |
| <div class="table-cols form-inline top-border-clear pdt10"> | |
| <?= gd_select_box('dialogSmsAutoCode', 'dialogSmsAutoCode', $templateCategoryAutoCodes, null, null, '카테고리 선택') ?> | |
| <input type="text" class="form-control width-xl js-maxlength" placeholder="템플릿명을 입력하여 주세요." name="dialogSubject" maxlength="10"> | |
| </div> | |
| <div class="pdt30 center"> | |
| <button type="button" class="btn btn-red btn-lg js-btn-dialog-save">저장</button> | |
변경 전sms_template.php
| <tbody> | |
| <?php if (count($templates) > 0): ?> | |
| <?php foreach ($templates as $template): ?> | |
| <tr data-sno="<?= $template['sno'] ?>" data-status="<?= $template['approvalStatus'] ?>" data-used="<?= $template['usedFl'] ?>"> | |
| <td class="text-center"> | |
| <input type="checkbox" name="sno[]" value="<?= $template['sno'] ?>"/> | |
| </td> | |
| if (!assertWritable()) { | |
| return; | |
| } | |
| if ($(e.target).closest('tr').data('status') === 'PENDING') { | |
| dialog_alert('<?= $notModifiableMessage ?>', '정보'); | |
| return; | |
변경 후sms_template.php
| <tbody> | |
| <?php if (count($templates) > 0): ?> | |
| <?php foreach ($templates as $template): ?> | |
| <tr data-sno="<?= $template['sno'] ?>" data-status="<?= $template['approvalStatus'] ?>" data-used="<?= $template['usedFl'] ?>" data-basic="<?= $template['isBasic'] ? 'y' : 'n' ?>"> | |
| <td class="text-center"> | |
| <input type="checkbox" name="sno[]" value="<?= $template['sno'] ?>"/> | |
| </td> | |
| if (!assertWritable()) { | |
| return; | |
| } | |
| if ($(e.target).closest('tr').data('basic') === 'y') { | |
| dialog_alert('<?= $basicNotModifiableMessage ?>', '정보'); | |
| return; | |
| } | |
| if ($(e.target).closest('tr').data('status') === 'PENDING') { | |
| dialog_alert('<?= $notModifiableMessage ?>', '정보'); | |
| return; | |
| <form id="frmSearch" method="get" class="js-form-enter-submit"> | |
| <div class="page-header js-affix"> | |
| <h3><?= end($naviMenu->location) ?></h3> | |
| <div class="btn-group"> | |
| <button type="button" class="btn btn-red js-template-register">+ 템플릿 등록</button> | |
| </div> | |
| </div> | |
| <div class="table-title"> | |
| SMS 템플릿 목록 검색 | |
| </div> | |
| <div class="search-detail-box"> | |
| <table class="table table-cols"> | |
| <colgroup> | |
| <col class="width-sm"/> | |
| <col/> | |
| <col class="width-sm"/> | |
| <col/> | |
| </colgroup> | |
| <tbody> | |
| <tr> | |
| <th>검색어</th> | |
| <td colspan="3" class="form-inline"> | |
| <?= gd_select_box(null, 'key', $keyList, '', $search['key']) ?> | |
| <input type="text" name="keyword" value="<?= $search['keyword'] ?>" class="form-control width-xl" placeholder="키워드를 입력해 주세요."/> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th>기간검색</th> | |
| <td colspan="3"> | |
| <div class="form-inline"> | |
| <span class="mgr10">등록일</span> | |
| <div class="input-group js-datepicker"> | |
| <input type="text" name="regDt[]" value="<?= $search['dateFrom'] ?>" class="form-control width-xs" placeholder="수기입력 가능"/> | |
| <span class="input-group-addon"><span class="btn-icon-calendar"></span></span> | |
| </div> | |
| ~ | |
| <div class="input-group js-datepicker"> | |
| <input type="text" name="regDt[]" value="<?= $search['dateTo'] ?>" class="form-control width-xs" placeholder="수기입력 가능"/> | |
| <span class="input-group-addon"><span class="btn-icon-calendar"></span></span> | |
| </div> | |
| <div class="btn-group js-dateperiod" data-toggle="buttons" data-target-name="regDt"> | |
| <?php foreach ($periodList as $days => $label): ?> | |
| <?php $isActive = $periodDays === $days; ?> | |
| <label class="btn btn-white btn-sm hand<?= $isActive ? ' active' : '' ?>"><input type="radio" name="periodFl" value="<?= $days ?>" <?= $isActive ? 'checked' : '' ?>/><?= $label ?></label> | |
| <?php endforeach; ?> | |
| </div> | |
| </div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th>카테고리</th> | |
| <td> | |
| <?= gd_select_box(null, 'category', $categoryList, '', $search['category'], '=전체보기=') ?> | |
| </td> | |
| <th>검수 상태</th> | |
| <td> | |
| <?= gd_select_box(null, 'approvalStatus', $approvalStatusList, '', $search['approvalStatus'], '=전체보기=') ?> | |
| </td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <div class="table-btn"> | |
| <input type="submit" value="검색" class="btn btn-lg btn-black"/> | |
| </div> | |
| <div class="table-header"> | |
| <div class="pull-left"> | |
| <?= gd_display_search_result($page->recode['total'], $page->recode['amount'], '개') ?> | |
| </div> | |
| <div class="pull-right"> | |
| <ul> | |
| <li><?= gd_select_box('sort', 'sort', $sortList, null, $search['sort'], null) ?></li> | |
| <li><?= gd_select_box_by_page_view_count($search['pageNum']) ?></li> | |
| </ul> | |
| </div> | |
| </div> | |
| </form> | |
| <form id="frmList" method="post"> | |
| <input type="hidden" name="mode" value=""/> | |
| <div class="mgb10"> | |
| <button type="button" class="btn btn-sm btn-white js-template-approve">선택 승인</button> | |
| <button type="button" class="btn btn-sm btn-white js-template-reject">선택 반려</button> | |
| </div> | |
| <table class="table table-rows table-fixed member-sms-template"> | |
| <colgroup> | |
| <col class="width5p"/> | |
| <col class="width10p"/> | |
| <col class="width10p"/> | |
| <col class="width25p"/> | |
| <col class="width10p"/> | |
| <col class="width10p"/> | |
| <col class="width10p"/> | |
| <col class="width5p"/> | |
| <col class="width5p"/> | |
| <col class="width10p"/> | |
| </colgroup> | |
| <thead> | |
| <tr> | |
| <th><input type="checkbox" class="js-check-all"/></th> | |
| <th>템플릿 코드</th> | |
| <th>카테고리</th> | |
| <th>템플릿명</th> | |
| <th>검수 상태</th> | |
| <th>등록일</th> | |
| <th>사용 중인 자동 알림</th> | |
| <th>수정</th> | |
| <th>삭제</th> | |
| <th>승인 이력</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <?php if (count($templates) > 0): ?> | |
| <?php foreach ($templates as $template): ?> | |
| <tr data-sno="<?= $template['sno'] ?>" data-status="<?= $template['approvalStatus'] ?>" data-used="<?= $template['usedFl'] ?>"> | |
| <td class="text-center"> | |
| <input type="checkbox" name="sno[]" value="<?= $template['sno'] ?>"/> | |
| </td> | |
| <td class="text-center"> | |
| <button type="button" class="btn-link js-template-preview"><?= $template['templateCode'] ?></button> | |
| </td> | |
| <td class="text-center"><?= $template['categoryName'] ?></td> | |
| <td><?= $template['templateName'] ?></td> | |
| <td class="text-center"><?= $template['statusName'] ?></td> | |
| <td class="text-center number multirows"><?= str_replace(' ', '<br/>', $template['regDt']) ?></td> | |
| <td class="text-center"> | |
| <?php if ($template['isUsedByAutoSend']): ?> | |
| <button type="button" class="btn btn-xs btn-gray js-template-auto-send">보기</button> | |
| <?php else: ?> | |
| 미사용 | |
| <?php endif; ?> | |
| </td> | |
| <td class="text-center"> | |
| <?php if (($template['isUserBasic'] ?? 'n') !== 'y'): ?> | |
| <button type="button" class="btn btn-xs btn-gray js-template-modify">수정</button> | |
| <?php endif; ?> | |
| </td> | |
| <td class="text-center"> | |
| <?php if (($template['isUserBasic'] ?? 'n') !== 'y'): ?> | |
| <button type="button" class="btn btn-xs btn-gray js-template-delete">삭제</button> | |
| <?php endif; ?> | |
| </td> | |
| <td class="text-center"> | |
| <button type="button" class="btn btn-xs btn-gray js-template-history">상세</button> | |
| </td> | |
| </tr> | |
| <?php endforeach; ?> | |
| <?php else: ?> | |
| <tr> | |
| <td colspan="10" class="no-data">검색된 정보가 없습니다.</td> | |
| </tr> | |
| <?php endif; ?> | |
| </tbody> | |
| </table> | |
| </form> | |
| <div class="center"><?= $page->getPage() ?></div> | |
| <script type="text/javascript"> | |
| <!-- | |
| $(document).ready(function () { | |
| var hasApprovalPermission = <?= $hasApprovalPermission ? 'true' : 'false' ?>; | |
| var hasWritePermission = <?= $hasWritePermission ? 'true' : 'false' ?>; | |
| function assertWritable() { | |
| if (!hasWritePermission) { | |
| dialog_alert('<?= $noWritePermissionMessage ?>'); | |
| } | |
| return hasWritePermission; | |
| } | |
| function snoOf(target) { | |
| return $(target).closest('tr').data('sno'); | |
| } | |
| function selectedSnoList() { | |
| return $('input[name="sno[]"]:checked').map(function () { | |
| return this.value; | |
| }).get(); | |
| } | |
| function selectedSnoListByStatus(status) { | |
| return $('input[name="sno[]"]:checked').filter(function () { | |
| return $(this).closest('tr').data('status') === status; | |
| }).map(function () { | |
| return this.value; | |
| }).get(); | |
| } | |
| var LAYER_FAIL_MESSAGE = '일시적인 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.'; | |
| // 진입 제한 문구도 <br> 포함이라 자식 엘리먼트 유무로 구분 불가 — layout_layer.php 의 #layer-wrap 유무로 판별 | |
| function isLayerHtml(html) { | |
| var $nodes = $($.parseHTML($.trim(String(html)), false)); | |
| return $nodes.filter('#layer-wrap').length > 0 || $nodes.find('#layer-wrap').length > 0; | |
| } | |
| function openLayer(url, title, data, size) { | |
| $.post(url, data, function (html) { | |
| if (!isLayerHtml(html)) { | |
| dialog_alert(LAYER_FAIL_MESSAGE, '정보'); | |
| return; | |
| } | |
| layer_popup(html, title, size); | |
| }).fail(function (xhr) { | |
| var response = xhr.responseJSON; | |
| dialog_alert(xhr.status === 403 && response && response.message ? response.message : LAYER_FAIL_MESSAGE, '정보'); | |
| }); | |
| } | |
| function requestProcess(data, confirmMessage, subMessage, confirmTitle) { | |
| function send() { | |
| $.ajax({ | |
| url: './sms_template_ps.php', | |
| type: 'post', | |
| dataType: 'json', | |
| data: data, | |
| success: function (response) { | |
| dialog_alert(response.message, '정보', {isReload: true}); | |
| }, | |
| error: function (xhr) { | |
| var response = xhr.responseJSON; | |
| dialog_alert(response && response.message ? response.message : '처리중에 오류가 발생하였습니다.', '정보'); | |
| } | |
| }); | |
| } | |
| if (!confirmMessage) { | |
| send(); | |
| return; | |
| } | |
| dialog_confirm(confirmMessage + (subMessage ? '<br><br>' + subMessage : ''), function (isConfirm) { | |
| if (isConfirm) { | |
| send(); | |
| } | |
| }, confirmTitle); | |
| } | |
| $('.js-check-all').click(function () { | |
| $('input[name="sno[]"]').prop('checked', this.checked); | |
| }); | |
| $('.js-template-register').click(function () { | |
| if (!assertWritable()) { | |
| return; | |
| } | |
| openLayer('./layer_sms_template_form.php', 'SMS 발송 템플릿 등록', {}, 'wide'); | |
| }); | |
| $('.js-template-modify').click(function (e) { | |
| if (!assertWritable()) { | |
| return; | |
| } | |
| if ($(e.target).closest('tr').data('status') === 'PENDING') { | |
| dialog_alert('<?= $notModifiableMessage ?>', '정보'); | |
| return; | |
| } | |
| if ($(e.target).closest('tr').data('used') === 'y') { | |
| dialog_alert('<?= $linkedNotModifiableMessage ?>', '정보'); | |
| return; | |
| } | |
| openLayer('./layer_sms_template_form.php', 'SMS 발송 템플릿 수정', {sno: snoOf(e.target)}, 'wide'); | |
| }); | |
| $('.js-template-preview').click(function (e) { | |
| openLayer('./layer_sms_template_preview.php', 'SMS 템플릿 미리보기', {sno: snoOf(e.target)}); | |
| }); | |
| $('.js-template-auto-send').click(function (e) { | |
| openLayer('./layer_sms_template_auto_send.php', '사용 중인 자동 알림', {sno: snoOf(e.target)}); | |
| }); | |
| $('.js-template-history').click(function (e) { | |
| openLayer('./layer_sms_template_approval_history.php', 'SMS 발송 템플릿 승인 이력', {sno: snoOf(e.target)}, 'wide'); | |
| }); | |
| $('.js-template-delete').click(function (e) { | |
| if (!assertWritable()) { | |
| return; | |
| } | |
| requestProcess( | |
| {mode: 'delete', sno: [snoOf(e.target)]}, | |
| '정말 삭제하시겠습니까?', | |
| null, | |
| 'SMS 템플릿 삭제' | |
| ); | |
| }); | |
| $('.js-template-approve, .js-template-reject').click(function (e) { | |
| if (!hasApprovalPermission) { | |
| dialog_alert('SMS 템플릿 승인/반려 권한이 없습니다. 권한은 대표운영자에게 문의하시기 바랍니다.', '경고'); | |
| return; | |
| } | |
| if (!assertWritable()) { | |
| return; | |
| } | |
| var snoList = selectedSnoList(); | |
| if (snoList.length < 1) { | |
| dialog_alert('선택하신 템플릿이 없습니다.', '정보'); | |
| return; | |
| } | |
| var isApprove = $(e.target).hasClass('js-template-approve'); | |
| var actionLabel = isApprove ? '승인' : '반려'; | |
| var pendingList = selectedSnoListByStatus('PENDING'); | |
| if (pendingList.length < 1) { | |
| dialog_alert(snoList.length === 1 | |
| ? '이미 승인 또는 반려 처리가 된 신청 건입니다.<br/>페이지 새로고침 후 확인해 주세요.' | |
| : '선택한 템플릿 중 검수 ' + actionLabel + ' 가능한 건이 없습니다. 검수중 상태의 템플릿만 처리할 수 있습니다.', '정보'); | |
| return; | |
| } | |
| var tailMessage = isApprove | |
| ? '승인한 템플릿은 승인 취소가 불가합니다.' | |
| : '반려한 템플릿은 수정 후 재검수가 가능합니다.'; | |
| var subMessage = '선택한 ' + snoList.length + '건 중 검수중 ' + pendingList.length + '건을 ' + actionLabel + '합니다. ' | |
| + '(승인 ' + selectedSnoListByStatus('APPROVED').length + '건·반려 ' + selectedSnoListByStatus('REJECTED').length + '건 제외). ' | |
| + tailMessage; | |
| requestProcess( | |
| {mode: isApprove ? 'approve' : 'reject', sno: pendingList}, | |
| '선택한 템플릿을 ' + actionLabel + '하시겠습니까?', | |
| subMessage, | |
| 'SMS 템플릿 ' + actionLabel | |
| ); | |
| }); | |
| $('select[name=\'sort\']').change({targetForm: '#frmSearch'}, member.page_sort); | |
| $('select[name=\'pageNum\']').change({targetForm: '#frmSearch'}, member.page_number); | |
| }); | |
| //--> | |
| </script> | |
변경 전_manage_permission_menu_list.php
| ?> | |
| </table> | |
| </div> | |
| <input type="hidden" id="messageSenderMeta" data-editor-approver="<?= !empty($isEditorMessageApprover) ? 'y' : 'n'; ?>" data-editor-representative="<?= !empty($isEditorMessageRepresentative) ? 'y' : 'n'; ?>" data-caller-blocked="<?= !empty($isCallerBlocked) ? 'y' : 'n'; ?>" data-sender-count="<?= (int)(gd_isset($messageSenderCount, 0)); ?>" data-sender-limit="<?= (int)(gd_isset($messageSenderLimit, 0)); ?>"/> | |
| <div class="access-control-reason <?=(!empty($sno) || $changeType == 'init') ? '' : 'display-none'?>" style="clear: both;"> | |
| <div class="table-action-dropdown"> | |
| <div class="table-action mgt0 mgb15"> | |
변경 후_manage_permission_menu_list.php
| ?> | |
| </table> | |
| </div> | |
| <input type="hidden" id="messageSenderMeta" data-editor-approver="<?= !empty($isEditorMessageApprover) ? 'y' : 'n'; ?>" data-editor-representative="<?= !empty($isEditorMessageRepresentative) ? 'y' : 'n'; ?>" data-caller-blocked="<?= !empty($isCallerBlocked) ? 'y' : 'n'; ?>" data-caller-uncertified-delegate="<?= !empty($isCallerUncertifiedDelegate) ? 'y' : 'n'; ?>" data-sender-count="<?= (int)(gd_isset($messageSenderCount, 0)); ?>" data-sender-limit="<?= (int)(gd_isset($messageSenderLimit, 0)); ?>"/> | |
| <div class="access-control-reason <?=(!empty($sno) || $changeType == 'init') ? '' : 'display-none'?>" style="clear: both;"> | |
| <div class="table-action-dropdown"> | |
| <div class="table-action mgt0 mgb15"> | |
변경 전manage_permission.php
| dialog_alert('SMS 발송 권한은 발신관리책임자 또는 발신관리위임자만 부여할 수 있습니다.<br>해당 권한을 해제한 후 다시 저장해 주세요.', '경고'); | |
| return false; | |
| } | |
| if ($smsMeta.data('editorRepresentative') === 'y' && $smsMeta.data('callerBlocked') === 'y') { | |
| dialog_alert('SMS 발송 메뉴 접근이 제한되었습니다.<br>스팸 또는 부정 사용으로 차단되어 접근할 수 없습니다.<br>자세한 사항은 1:1문의로 문의하시기 바랍니다.', '경고'); | |
| return false; | |
변경 후manage_permission.php
| dialog_alert('SMS 발송 권한은 발신관리책임자 또는 발신관리위임자만 부여할 수 있습니다.<br>해당 권한을 해제한 후 다시 저장해 주세요.', '경고'); | |
| return false; | |
| } | |
| if ($smsMeta.data('callerUncertifiedDelegate') === 'y') { | |
| dialog_alert('SMS 발송 권한은 발신관리책임자 또는 발신관리위임자만 부여할 수 있습니다.<br>본인인증을 완료해야 발신관리위임자 역할을 수행할 수 있습니다.<br>운영자 수정 페이지에서 본인인증을 먼저 완료하시기 바랍니다.', '경고'); | |
| return false; | |
| } | |
| if ($smsMeta.data('editorRepresentative') === 'y' && $smsMeta.data('callerBlocked') === 'y') { | |
| dialog_alert('SMS 발송 메뉴 접근이 제한되었습니다.<br>스팸 또는 부정 사용으로 차단되어 접근할 수 없습니다.<br>자세한 사항은 1:1문의로 문의하시기 바랍니다.', '경고'); | |
| return false; | |
변경 전manage_register.php
| <tbody> | |
| <?php if (!empty($messageDelegate['visible'])): // SMS 발신 위임자 권한 (권한 설정 테이블 row, 지정/회수는 최고운영자만) ?> | |
| <tr class="js-message-delegate-area"> | |
| <th>SMS 발신 위임자 권한 설정</th> | |
| <td class="form-inline"> | |
| <input type="hidden" name="messageDelegateAction" id="messageDelegateAction" value=""> | |
| <?php if ($messageDelegate['assigned'] === true): ?> | |
| <span class="notice-info">미지정</span> | |
| <?php endif; ?> | |
| <br> | |
| <span class="notice-info">문자를 발송할 '발송담당자' 지정은 위임과 다르므로 <a href="/policy/manage_permission.php" target="_blank" class="btn-link">[설정 > 관리 정책 > 운영자 관리]</a> 페이지의 회원 추가설정 중 'SMS 발송' 권한을 설정해 주세요.</span> | |
| </td> | |
| </tr> | |
| <?php if ($messageDelegate['assigned'] === false && $messageDelegate['editable'] === true): ?> | |
| <tr class="js-message-delegate-set-reason-row" style="display:none;"> | |
| <th>SMS 발신 위임자 권한 설정 사유</th> | |
| <td> | |
| <input type="text" id="messageDelegateSetReason" class="form-control width-2xl" maxlength="50"> | |
| <span class="notice-info">SMS 발송 위임 권한을 설정하기 위한 명확한 사유를 입력해 주세요.</span> | |
| <?php endif; ?> | |
| <?php if ($messageDelegate['assigned'] === true && $messageDelegate['editable'] === true): ?> | |
| <tr class="js-message-delegate-release-reason-row" style="display:none;"> | |
| <th>SMS 발신 위임자 권한 해제 사유</th> | |
| <td> | |
| <input type="text" id="messageDelegateReleaseReason" class="form-control width-2xl" maxlength="50"> | |
| <span class="notice-info">SMS 발송 위임 권한을 해제하기 위한 명확한 사유를 입력해 주세요.</span> | |
| }); | |
| }); | |
| $('.js-check-authno').click(function () { | |
| var cell_phone_auth_no = $('input[name=cellPhoneAuthNo]').val(); | |
| if (!manage_register.regex.auth_no.test(cell_phone_auth_no)) { | |
변경 후manage_register.php
| <tbody> | |
| <?php if (!empty($messageDelegate['visible'])): // SMS 발신 위임자 권한 (권한 설정 테이블 row, 지정/회수는 최고운영자만) ?> | |
| <tr class="js-message-delegate-area"> | |
| <th>발신관리위임자 권한 설정</th> | |
| <td class="form-inline"> | |
| <input type="hidden" name="messageDelegateAction" id="messageDelegateAction" value=""> | |
| <?php if ($messageDelegate['assigned'] === true): ?> | |
| <span class="notice-info">미지정</span> | |
| <?php endif; ?> | |
| <br> | |
| <span class="notice-info">문자를 발송할 '발송담당자' 지정은 위임과 다르므로 <a href="/policy/manage_permission.php" target="_blank" class="btn-link">[기본설정 > 관리 정책 > 운영자 권한 설정]</a> 페이지의 회원 추가설정 중 'SMS 발송' 권한을 설정해 주세요.</span> | |
| </td> | |
| </tr> | |
| <?php if ($messageDelegate['assigned'] === false && $messageDelegate['editable'] === true): ?> | |
| <tr class="js-message-delegate-set-reason-row" style="display:none;"> | |
| <th>발신관리위임자 권한 설정 사유</th> | |
| <td> | |
| <input type="text" id="messageDelegateSetReason" class="form-control width-2xl" maxlength="50"> | |
| <span class="notice-info">SMS 발송 위임 권한을 설정하기 위한 명확한 사유를 입력해 주세요.</span> | |
| <?php endif; ?> | |
| <?php if ($messageDelegate['assigned'] === true && $messageDelegate['editable'] === true): ?> | |
| <tr class="js-message-delegate-release-reason-row" style="display:none;"> | |
| <th>발신관리위임자 권한 해제 사유</th> | |
| <td> | |
| <input type="text" id="messageDelegateReleaseReason" class="form-control width-2xl" maxlength="50"> | |
| <span class="notice-info">SMS 발송 위임 권한을 해제하기 위한 명확한 사유를 입력해 주세요.</span> | |
| }); | |
| }); | |
| $('input[name="cellPhone"]').on('input', function () { | |
| $('.my-sms-auth').text('(미인증)'); | |
| }); | |
| $('.js-check-authno').click(function () { | |
| var cell_phone_auth_no = $('input[name=cellPhoneAuthNo]').val(); | |
| if (!manage_register.regex.auth_no.test(cell_phone_auth_no)) { | |
변경 전manage_security.php
| 화면보안접속 사용 시 보안접속 인증화면이 노출되는 화면<br> | |
| - 기본설정>관리정책>운영자관리/운영자등록<br> | |
| - 기본설정>관리정책>운영보안설정<br> | |
| - 기본설정>결제정책>무통장입금은행관리<br> | |
| - 회원>SMS관리>개별/전체 SMS 발송 | |
| </p> | |
| </div> | |
| </td> | |
변경 후manage_security.php
| 화면보안접속 사용 시 보안접속 인증화면이 노출되는 화면<br> | |
| - 기본설정>관리정책>운영자관리/운영자등록<br> | |
| - 기본설정>관리정책>운영보안설정<br> | |
| - 기본설정>결제정책>무통장입금은행관리 | |
| </p> | |
| </div> | |
| </td> | |
변경 전cart_remind_list.php
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script> | |
| <div class="page-header js-affix"> | |
| <h3><?php echo end($naviMenu->location); ?> | |
| </h3> | |
| menual_send(); | |
| } | |
| function menual_send() { | |
| var params = { | |
| mode: 'sendCartRemindManual', | |
| cartRemindNo: cartRemindNo, | |
변경 후cart_remind_list.php
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script> | |
| <script src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-send-gate.js') ?>"></script> | |
| <div class="page-header js-affix"> | |
| <h3><?php echo end($naviMenu->location); ?> | |
| </h3> | |
| menual_send(); | |
| } | |
| function menual_send() { | |
| if (window.__cartRemindGatePassed !== true) { | |
| SmsSendGate.ensure().then(function () { | |
| window.__cartRemindGatePassed = true; | |
| menual_send(); | |
| window.__cartRemindGatePassed = false; | |
| }).catch(function () {}); | |
| return; | |
| } | |
| var params = { | |
| mode: 'sendCartRemindManual', | |
| cartRemindNo: cartRemindNo, | |
변경 전comeback_coupon_list.php
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script> | |
| <div class="page-header js-affix"> | |
| <h3><?php echo end($naviMenu->location); ?></h3> | |
| <div class="btn-group"> | |
| } | |
| }); | |
| function sendAction(sno, couponNo) { | |
| var params = { | |
| mode : 'ajaxComebackCouponActionCount', | |
| var smsCount = $('#sms-point').val(); | |
| var smsFl = $('#dataInfo' + sno).attr('data-smsFl'); | |
| $.ajax({ | |
| method: "POST", | |
| cache: false, | |
변경 후comeback_coupon_list.php
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script> | |
| <script src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-send-gate.js') ?>"></script> | |
| <div class="page-header js-affix"> | |
| <h3><?php echo end($naviMenu->location); ?></h3> | |
| <div class="btn-group"> | |
| } | |
| }); | |
| var comebackCouponGatePassed = false; | |
| function sendAction(sno, couponNo) { | |
| var params = { | |
| mode : 'ajaxComebackCouponActionCount', | |
| var smsCount = $('#sms-point').val(); | |
| var smsFl = $('#dataInfo' + sno).attr('data-smsFl'); | |
| if (smsFl === 'y' && comebackCouponGatePassed !== true) { | |
| SmsSendGate.ensure().then(function () { | |
| comebackCouponGatePassed = true; | |
| sendAction(sno, couponNo); | |
| comebackCouponGatePassed = false; | |
| }).catch(function () {}); | |
| return false; | |
| } | |
| $.ajax({ | |
| method: "POST", | |
| cache: false, | |
변경 전layer_message_representative_register_guide.php
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/manager-certification.js') ?>"></script> | |
| <link rel="stylesheet" href="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'css/layer-message-representative-register-guide.css') ?>"/> | |
| <?php $guideAlt = '발신관리책임자 등록 안내 - 우리 쇼핑몰 번호, 도용되지 않게 지켜드려요. 발신관리책임자와 담당자 정보로 발신번호 사용을 관리해 다른 사람이 SMS를 보내지 못하게 막아줘요. 등록은 한 번 1분이면 완료, 등록 시 최고운영자 정보가 통합회원 정보로 자동 변경(등록 이후 이름·휴대폰번호·이메일은 통합회원 마이페이지에서만 수정할 수 있어요), 26년 10월 25일까지 등록 필수(미등록 시 SMS 관련 기능 이용이 제한됩니다). 공지사항에서 등록 방법, 미등록 시 제한 사항에 대해 자세하게 확인하실 수 있습니다. 급증하는 불법스팸·스미싱 피해 예방을 위한 전기통신사업법 제22조의11 및 전송자격인증 기준 등에 관한 고시(방통위 고시 제2026-1호) 개정에 따른 조치입니다.'; ?> | |
| <div id="popupNoticeRepresentativeRegisterGuide"> | |
| <div class="guide_overlay"></div> | |
| <div class="guide_content"> | |
| <div class="guide_head"> | |
| <p class="tit">발신관리책임자 등록 안내</p> | |
| </div> | |
| <div class="guide_body"> | |
| <?php if (!empty($noticeUri)) { ?> | |
| <a href="<?= $noticeUri ?>" target="_blank" class="guide_notice"> | |
| <img src="/admin/gd_share/img/panel/representative_guide.png" alt="<?= $guideAlt ?>"/> | |
| </a> | |
| <?php } else { ?> | |
| <img src="/admin/gd_share/img/panel/representative_guide.png" alt="<?= $guideAlt ?>"/> | |
| <?php } ?> | |
| <div class="btn_guide_area"> | |
| <button type="button" class="js-manager-modify">통합회원 정보 확인</button> | |
| <button type="button" class="js-representative-register">등록하기</button> | |
| </div> | |
| </div> | |
| <div class="bot_gray_close"> | |
| <input type="checkbox" title="7일동안 보지 않기" onclick="adminPanelCookie('<?= $displayCookie ?>', <?= $displayCookieExpireDay ?>, this);$('#popupNoticeRepresentativeRegisterGuide').hide();"/>7일동안 보지 않기 | |
| <input type="checkbox" title="닫기" onclick="$('#popupNoticeRepresentativeRegisterGuide').hide();"/>닫기 | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| (function () { | |
| var isCommerceConnected = <?= json_encode($isCommerceConnected) ?>; | |
| var isSelfCertified = <?= json_encode($isSelfCertified) ?>; | |
| var isCommerceLoginRequired = <?= json_encode($isCommerceLoginRequired) ?>; | |
| // 등록 시 최고운영자 정보가 통합회원 정보로 덮어써지므로 솔루션 운영자 수정 화면이 아니라 통합회원 마이페이지로 보낸다 | |
| $('.js-manager-modify').on('click', function () { | |
| window.open('https://accounts.godo.co.kr/mypage/acc', '_blank'); | |
| }); | |
| $('.js-representative-register').on('click', function () { | |
| $('#popupNoticeRepresentativeRegisterGuide').hide(); | |
| // 미연동 최고운영자는 기존 통합회원 연동 프로세스로 보낸다 (복귀 주소는 컨트롤러가 URL 에 담아둔다) | |
| if (!isCommerceConnected) { | |
| const message = '<b>발신관리책임자 등록을 위해 통합회원 연동을 진행합니다.</b><br>통합회원 연동을 한 최고운영자만 발신관리책임자 등록이 가능합니다.'; | |
| const commerceLoginUri = <?= json_encode($commerceLoginUri) ?>; | |
| // 안내를 닫는 모든 경로에서 연동 페이지로 이어져야 한다 | |
| managerCertification.closableAlert(message, '안내', function () { | |
| location.href = commerceLoginUri; | |
| }); | |
| return; | |
| } | |
| // 이미 본인인증한 운영자도 일반 로그인이면 등록을 진행시키지 않는다 — 로그인 방식은 인증 이력과 무관하게 판정한다 | |
| if (isCommerceLoginRequired) { | |
| managerCertification.alertCommerceLoginRequired(); | |
| return; | |
| } | |
| // 본인인증 이력이 없으면 인증부터 진행한다 | |
| if (!isSelfCertified) { | |
| managerCertification.certifyWithGuide({onSuccess: checkBlockAndRegister}); | |
| return; | |
| } | |
| // 이미 본인인증(솔루션 diValue 존재)한 운영자는 곧바로 등록 흐름을 탄다 | |
| checkBlockAndRegister(); | |
| }); | |
| // 인증을 마쳐도 차단된 책임자면 등록을 진행시키지 않는다 | |
| function checkBlockAndRegister() { | |
| $.post('/share/message_representative_register_ps.php', {mode: 'checkBlock'}, function (response) { | |
| if (!response || response.success !== true) { | |
| showRequestFailed(response); | |
| return; | |
| } | |
| if (response.data.blocked) { | |
| dialog_alert(response.data.message, '안내'); | |
| return; | |
| } | |
| checkMigrationCaller(); | |
| }, 'json').fail(function () { | |
| showRequestFailed(); | |
| }); | |
| } | |
| // 통합회원 전환 전에 등록해 둔 기존 발신관리책임자가 있으면 이관 여부를 묻는다 | |
| function checkMigrationCaller() { | |
| $.post('/share/message_representative_register_ps.php', {mode: 'checkMigration'}, function (response) { | |
| if (!response || response.success !== true) { | |
| showRequestFailed(response); | |
| return; | |
| } | |
| if (response.data.exists) { | |
| dialog_confirm( | |
| '발신관리책임자 등록이 완료됐습니다.<br>이전에 사용하던 발신번호를 가져오겠습니까?<br>가져오기를 완료해야 새로운 발신번호를 등록할 수 있습니다.', | |
| function (confirmed) { | |
| if (!confirmed) { | |
| showRegisterCompleted(); | |
| return; | |
| } | |
| SmsCallerHubLayer.openMigration({onCompleted: showRegisterCompleted}); | |
| }, | |
| '이전 발신번호 가져오기' | |
| ); | |
| return; | |
| } | |
| showRegisterCompleted(); | |
| }, 'json').fail(function () { | |
| showRequestFailed(); | |
| }); | |
| } | |
| // 응답이 비어 있거나 파싱에 실패하면 백단 문구가 없으므로 기본 문구로 안내한다 | |
| function showRequestFailed(response) { | |
| dialog_alert((response && response.message) || '일시적인 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.', '안내'); | |
| } | |
| // 이관하지 않고 끝나는 경로는 모두 등록 완료를 알린다 | |
| function showRegisterCompleted() { | |
| dialog_alert( | |
| "발신관리책임자 등록이 완료되었습니다.<br><br>최고운영자 정보(이름·휴대폰번호·이메일)는 이제 통합회원 마이페이지에서만 수정할 수 있습니다.<br>부운영자와 함께 SMS 업무를 하려면 '발신관리위임자'로 설정하거나 'SMS 발송'권한을 부여해 주세요.", | |
| '안내' | |
| ); | |
| } | |
| })(); | |
| </script> | |
변경 후layer_message_representative_register_guide.php
변경 전layer_message_representative_send_restrict.php
변경 후layer_message_representative_send_restrict.php
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'ncds/script/common/aggregator-message-router.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-caller-hub-layer.js') ?>"></script> | |
| <script defer src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/manager-certification.js') ?>"></script> | |
| <link rel="stylesheet" href="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'css/layer-message-representative-send-restrict.css') ?>"/> | |
| <div id="popupNoticeRepresentativeSendRestrict"> | |
| <div class="guide_overlay"></div> | |
| <div class="guide_content"> | |
| <div class="guide_head"> | |
| <p class="tit">SMS 메뉴 이용 제한</p> | |
| </div> | |
| <div class="guide_body"> | |
| <p class="restrict_caution">발신관리책임자 미등록으로 SMS 메뉴 접근과 발송 기능 이용이 제한된 상태입니다.</p> | |
| <ul class="restrict_list"> | |
| <li>발신관리책임자 등록을 완료하면 즉시 다시 이용할 수 있습니다.</li> | |
| <li>발신관리책임자 등록은 최고운영자만 할 수 있으며,<br>등록되면 최고운영자 정보가 <a href="https://accounts.godo.co.kr/mypage/acc" target="_blank" rel="noopener noreferrer"><strong>통합회원 정보</strong></a><strong>로 자동 변경</strong>됩니다.</li> | |
| <li><?= !empty($guideUri) ? '<a href="' . gd_htmlspecialchars($guideUri) . '" target="_blank">관련 가이드</a>' : '관련 가이드' ?>에서 등록 방법, 미등록 시 제한 사항에 대해 자세하게 확인하실 수 있습니다.</li> | |
| </ul> | |
| <p class="restrict_info">※ 급증하는 불법스팸·스미싱 피해 예방을 위한 전기통신사업법 제22조의11 및 전송자격인증 기준 등에 관한 고시(방통위 고시 제2026-1호) 개정에 따른 조치입니다.</p> | |
| <div class="btn_guide_area"> | |
| <?php if (!empty($isRegisterAvailable)) { ?> | |
| <button type="button" class="js-representative-register">발신관리책임자 등록</button> | |
| <?php } ?> | |
| </div> | |
| </div> | |
| <div class="bot_gray_close"> | |
| <input type="checkbox" title="7일동안 보지 않기" onclick="adminPanelCookie('<?= $displayCookie ?>', <?= $displayCookieExpireDay ?>, this);$('#popupNoticeRepresentativeSendRestrict').hide();"/>7일동안 보지 않기 | |
| <input type="checkbox" title="닫기" onclick="$('#popupNoticeRepresentativeSendRestrict').hide();"/>닫기 | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| (function () { | |
| var isCommerceConnected = <?= json_encode($isCommerceConnected) ?>; | |
| var isSelfCertified = <?= json_encode($isSelfCertified) ?>; | |
| var isCommerceLoginRequired = <?= json_encode($isCommerceLoginRequired) ?>; | |
| $('.js-representative-register').on('click', function () { | |
| $('#popupNoticeRepresentativeSendRestrict').hide(); | |
| // 미연동 최고운영자는 기존 통합회원 연동 프로세스로 보낸다 (복귀 주소는 컨트롤러가 URL 에 담아둔다) | |
| if (!isCommerceConnected) { | |
| const message = '<b>발신관리책임자 등록을 위해 통합회원 연동을 진행합니다.</b><br>통합회원 연동을 한 최고운영자만 발신관리책임자 등록이 가능합니다.'; | |
| const commerceLoginUri = <?= json_encode($commerceLoginUri) ?>; | |
| // 안내를 닫는 모든 경로에서 연동 페이지로 이어져야 한다 | |
| managerCertification.closableAlert(message, '안내', function () { | |
| location.href = commerceLoginUri; | |
| }); | |
| return; | |
| } | |
| // 이미 본인인증한 운영자도 일반 로그인이면 등록을 진행시키지 않는다 — 로그인 방식은 인증 이력과 무관하게 판정한다 | |
| if (isCommerceLoginRequired) { | |
| managerCertification.alertCommerceLoginRequired(); | |
| return; | |
| } | |
| // 본인인증 이력이 없으면 인증부터 진행한다 | |
| if (!isSelfCertified) { | |
| managerCertification.certifyWithGuide({onSuccess: checkBlockAndRegister}); | |
| return; | |
| } | |
| // 이미 본인인증(솔루션 diValue 존재)한 운영자는 곧바로 등록 흐름을 탄다 | |
| checkBlockAndRegister(); | |
| }); | |
| // 인증을 마쳐도 차단된 책임자면 등록을 진행시키지 않는다 | |
| function checkBlockAndRegister() { | |
| $.post('/share/message_representative_register_ps.php', {mode: 'checkBlock'}, function (response) { | |
| if (!response || response.success !== true) { | |
| showRequestFailed(response); | |
| return; | |
| } | |
| if (response.data.blocked) { | |
| dialog_alert(response.data.message, '안내'); | |
| return; | |
| } | |
| checkMigrationCaller(); | |
| }, 'json').fail(function () { | |
| showRequestFailed(); | |
| }); | |
| } | |
| // 통합회원 전환 전에 등록해 둔 기존 발신관리책임자가 있으면 이관 여부를 묻는다 | |
| function checkMigrationCaller() { | |
| $.post('/share/message_representative_register_ps.php', {mode: 'checkMigration'}, function (response) { | |
| if (!response || response.success !== true) { | |
| showRequestFailed(response); | |
| return; | |
| } | |
| if (response.data.exists) { | |
| dialog_confirm( | |
| '발신관리책임자 등록이 완료됐습니다.<br>이전에 사용하던 발신번호를 가져오겠습니까?<br>가져오기를 완료해야 새로운 발신번호를 등록할 수 있습니다.', | |
| function (confirmed) { | |
| if (!confirmed) { | |
| showRegisterCompleted(); | |
| return; | |
| } | |
| SmsCallerHubLayer.openMigration({onCompleted: showRegisterCompleted}); | |
| }, | |
| '이전 발신번호 가져오기' | |
| ); | |
| return; | |
| } | |
| showRegisterCompleted(); | |
| }, 'json').fail(function () { | |
| showRequestFailed(); | |
| }); | |
| } | |
| // 응답이 비어 있거나 파싱에 실패하면 백단 문구가 없으므로 기본 문구로 안내한다 | |
| function showRequestFailed(response) { | |
| dialog_alert((response && response.message) || '일시적인 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.', '안내'); | |
| } | |
| // 이관하지 않고 끝나는 경로는 모두 등록 완료를 알린다 | |
| function showRegisterCompleted() { | |
| managerCertification.registerCompletedAlert(function () { window.location.reload(); }); | |
| } | |
| })(); | |
| </script> | |
변경 전layer_occupancy_auth.php
변경 후layer_occupancy_auth.php
| <?php | |
| /* | |
| * Copyright (C) 2026 NHN COMMERCE. - All Rights Reserved | |
| * | |
| * Unauthorized copying or redistribution of this file in source and binary forms via any medium | |
| * is strictly prohibited. | |
| */ | |
| ?> | |
| <script type="text/javascript"> | |
| (function () { | |
| if (BootstrapDialog.occupancyAuthGuarded) { | |
| return; | |
| } | |
| BootstrapDialog.occupancyAuthGuarded = true; | |
| BootstrapDialog.closeAll = function () { | |
| $.each(BootstrapDialog.dialogs, function (id, instance) { | |
| var cssClass = instance.getCssClass ? String(instance.getCssClass() || '') : ''; | |
| if (cssClass.indexOf('occupancy-auth-dialog') === -1) { | |
| instance.close(); | |
| } | |
| }); | |
| }; | |
| }()); | |
| </script> | |
| <?php if (!empty($isLocked)): ?> | |
| <div class="notice-info js-occupancy-auth-caution"> | |
| <strong><?= $lockedMessage ?></strong> | |
| <br>잠금 해제를 위해서는 본인확인 절차를 진행해야 합니다. | |
| </div> | |
| <div class="text-center"> | |
| <button type="button" class="btn btn-lg btn-white js-layer-close">닫기</button> | |
| <button type="button" class="btn btn-black btn-lg js-unlock-occupancy-auth">본인 인증</button> | |
| </div> | |
| <script type="text/javascript"> | |
| (function () { | |
| var AUTH_CONTEXT = <?= json_encode($authContext, JSON_UNESCAPED_UNICODE) ?>; | |
| $('.js-unlock-occupancy-auth').on('click', function () { | |
| $.getScript('<?= PATH_ADMIN_GD_SHARE ?>script/manager-certification.js').done(function () { | |
| managerCertification.certify({ | |
| onComplete: function (success, reason) { | |
| if (success === false) { | |
| if (reason === 'mismatch') { | |
| // 불일치는 잠금 모달을 유지한 채 안내 문구만 바꿔 재인증을 유도한다 | |
| $('.js-occupancy-auth-caution').html('<strong>인증하신 정보가 등록된 운영자의 정보와 일치하지 않습니다. 다시 인증해 주세요.</strong>'); | |
| return; | |
| } | |
| alert('본인인증에 실패했습니다. 인증 정보를 확인 후 다시 시도해 주세요.'); | |
| return; | |
| } | |
| // 발송 화면에서는 리로드하면 작성 중인 발송 내용이 사라지므로 게이트만 다시 태운다 | |
| var reloadsAfterAuth = (AUTH_CONTEXT !== 'send' || typeof SmsSendGate === 'undefined'); | |
| if (reloadsAfterAuth && typeof window.occupancyAuthCompleted === 'function') { | |
| window.occupancyAuthCompleted(); | |
| $('.js-layer-close').trigger('click'); | |
| return; | |
| } | |
| $('.js-layer-close').trigger('click'); | |
| if (reloadsAfterAuth) { | |
| location.reload(); | |
| return; | |
| } | |
| SmsSendGate.ensure({}).catch(function () {}); | |
| } | |
| }); | |
| }); | |
| }); | |
| })(); | |
| </script> | |
| <?php else: ?> | |
| <?php | |
| $authTypeLabels = [ | |
| \Origin\Service\Sms\OccupancyAuth\OccupancyAuthService::AUTH_TYPE_SMS => 'SMS 인증', | |
| \Origin\Service\Sms\OccupancyAuth\OccupancyAuthService::AUTH_TYPE_EMAIL => '이메일 인증', | |
| ]; | |
| $authTypeTargets = [ | |
| \Origin\Service\Sms\OccupancyAuth\OccupancyAuthService::AUTH_TYPE_SMS => (string)gd_isset($authTargets['cellPhone'], ''), | |
| \Origin\Service\Sms\OccupancyAuth\OccupancyAuthService::AUTH_TYPE_EMAIL => (string)gd_isset($authTargets['email'], ''), | |
| ]; | |
| $authNotices = [ | |
| \Origin\Service\Sms\OccupancyAuth\OccupancyAuthService::CONTEXT_ENTRY => 'SMS 메뉴 접근 시 추가 인증이', | |
| \Origin\Service\Sms\OccupancyAuth\OccupancyAuthService::CONTEXT_SEND => 'SMS 발송 시 추가 인증이', | |
| ]; | |
| ?> | |
| <div class="notice-info"> | |
| 「전기통신사업법」 제22조의11 및 전송자격인증 기준(방송통신위원회 고시)에 따라 계정 탈취 및 무단 사용을 방지하기 위해 <?= gd_htmlspecialchars(gd_isset($authNotices[$authContext], $authNotices[\Origin\Service\Sms\OccupancyAuth\OccupancyAuthService::CONTEXT_ENTRY])) ?> 적용됩니다. | |
| 등록된 인증수단으로 본인 확인을 완료한 후 이용해 주세요. | |
| </div> | |
| <table class="table table-cols"> | |
| <tbody> | |
| <tr> | |
| <th>인증수단</th> | |
| <td> | |
| <?php if (empty($hasAuthTarget)): ?> | |
| <span class="text-red"><?= nl2br(gd_htmlspecialchars($noAuthTargetMessage)) ?></span> | |
| <?php else: ?> | |
| <?php foreach ($authTypeStates as $authType => $selectable): ?> | |
| <label class="control-label mgr10"> | |
| <input type="radio" name="occupancyAuthType" value="<?= gd_htmlspecialchars($authType) ?>" <?= gd_isset($checked['occupancyAuthType'][$authType]); ?> <?= gd_isset($disabled['occupancyAuthType'][$authType]); ?>/> | |
| <?= gd_htmlspecialchars(gd_isset($authTypeLabels[$authType], $authType)) ?> | |
| </label> | |
| <?php endforeach; ?> | |
| <p class="mgl5">운영자 관리 페이지에서 설정한 인증수단 정보로 인증번호가 발송됩니다.</p> | |
| <?php endif; ?> | |
| </td> | |
| </tr> | |
| <?php if (!empty($hasAuthTarget)): ?> | |
| <tr> | |
| <th>인증 정보</th> | |
| <td class="form-inline"> | |
| <strong id="occupancyAuthTarget" class="mgr10"><?= gd_htmlspecialchars(gd_isset($authTypeTargets[$selectedAuthType], '')) ?></strong> | |
| <button type="button" class="btn btn-sm btn-gray js-request-occupancy-otp">인증번호 발송</button> | |
| <strong class="text-red mgl10" id="occupancyCountDown"></strong> | |
| <span class="text-red mgl10" id="occupancyResendNotice"></span> | |
| </td> | |
| </tr> | |
| <tr id="occupancyAuthCodeRow" style="display:none;"> | |
| <th>인증번호</th> | |
| <td class="form-inline" id="occupancyAuthCodeField"> | |
| <input type="text" name="occupancyAuthCode" id="occupancyAuthCode" maxlength="6" class="form-control width-lg" placeholder="인증번호를 입력해주세요." autocomplete="off"> | |
| <span class="text-red mgl10" id="occupancyCodeNotice"></span> | |
| </td> | |
| </tr> | |
| <?php endif; ?> | |
| </tbody> | |
| </table> | |
| <div class="text-center"> | |
| <button type="button" class="btn btn-lg btn-white js-layer-close">닫기</button> | |
| <?php if (!empty($hasAuthTarget)): ?> | |
| <button type="button" class="btn btn-black btn-lg js-complete-occupancy-auth">인증완료</button> | |
| <?php endif; ?> | |
| </div> | |
| <script type="text/javascript"> | |
| (function () { | |
| var PS_URL = '../share/occupancy_auth_ps.php'; | |
| var LAYER_URL = '../share/layer_occupancy_auth.php'; | |
| var CODE_INPUT = '#occupancyAuthCode'; | |
| var CODE_ROW = '#occupancyAuthCodeRow'; | |
| var CODE_FIELD = '#occupancyAuthCodeField'; | |
| var CODE_NOTICE = '#occupancyCodeNotice'; | |
| var COUNT_DOWN = '#occupancyCountDown'; | |
| var RESEND_NOTICE = '#occupancyResendNotice'; | |
| var REQUEST_BUTTON = '.js-request-occupancy-otp'; | |
| var RESEND_LABEL = '재전송'; | |
| var CODE_PATTERN = /^[0-9]{1,6}$/; | |
| var AUTH_CONTEXT = <?= json_encode($authContext, JSON_UNESCAPED_UNICODE) ?>; | |
| var AUTH_TARGETS = <?= json_encode($authTypeTargets, JSON_UNESCAPED_UNICODE) ?>; | |
| var AUTH_TARGET = '#occupancyAuthTarget'; | |
| var resendInterval = <?= (int)$resendIntervalSeconds ?>; | |
| var popupSession = <?= (int)$popupSessionSeconds ?>; | |
| var otpTtl = <?= (int)$otpTtlSeconds ?>; | |
| var timerHandle = null; | |
| var sessionHandle = null; | |
| var resendHandle = null; | |
| var lastSentAt = 0; | |
| var restRemain = 0; | |
| function now() { | |
| return Math.floor(Date.now() / 1000); | |
| } | |
| function stopCountDown() { | |
| if (timerHandle) { | |
| clearInterval(timerHandle); | |
| timerHandle = null; | |
| } | |
| } | |
| function stopResendNotice() { | |
| if (resendHandle) { | |
| clearInterval(resendHandle); | |
| resendHandle = null; | |
| } | |
| $(RESEND_NOTICE).text(''); | |
| } | |
| function refreshResendNotice() { | |
| var rest = resendInterval - (now() - lastSentAt); | |
| if (rest <= 0) { | |
| stopResendNotice(); | |
| return; | |
| } | |
| $(RESEND_NOTICE).text(resendInterval + '초 후에 다시 시도해주세요.'); | |
| } | |
| function startResendCoolDown() { | |
| if (resendHandle) { | |
| clearInterval(resendHandle); | |
| resendHandle = null; | |
| } | |
| resendHandle = setInterval(refreshResendNotice, 1000); | |
| refreshResendNotice(); | |
| } | |
| function stopTimer() { | |
| stopCountDown(); | |
| stopResendNotice(); | |
| if (sessionHandle) { | |
| clearTimeout(sessionHandle); | |
| sessionHandle = null; | |
| } | |
| } | |
| function closeLayer() { | |
| stopTimer(); | |
| var $modal = $('.js-complete-occupancy-auth').closest('.modal'); | |
| if ($modal.length > 0) { | |
| $modal.modal('hide'); | |
| return; | |
| } | |
| $('.js-layer-close').trigger('click'); | |
| } | |
| function showLockedLayer() { | |
| stopTimer(); | |
| var $layerBody = $('.js-complete-occupancy-auth').closest('.bootstrap-dialog-message'); | |
| if ($layerBody.length === 0) { | |
| closeLayer(); | |
| return; | |
| } | |
| $.ajax({ | |
| url: LAYER_URL, | |
| data: {authContext: AUTH_CONTEXT}, | |
| cache: false, | |
| dataType: 'html' | |
| }).done(function (lockedHtml) { | |
| $layerBody.html(lockedHtml); | |
| }).fail(closeLayer); | |
| } | |
| function renderCountDown() { | |
| $(COUNT_DOWN).text(Math.floor(restRemain / 60) + ':' + ('0' + (restRemain % 60)).slice(-2)); | |
| } | |
| function tickCountDown() { | |
| restRemain -= 1; | |
| if (restRemain <= 0) { | |
| restRemain = 0; | |
| renderCountDown(); | |
| stopCountDown(); | |
| return; | |
| } | |
| renderCountDown(); | |
| } | |
| function startCountDown() { | |
| stopCountDown(); | |
| restRemain = otpTtl; | |
| renderCountDown(); | |
| timerHandle = setInterval(tickCountDown, 1000); | |
| } | |
| function selectedAuthType() { | |
| return $('input[name="occupancyAuthType"]:checked').val() || 'SMS'; | |
| } | |
| function refreshAuthTarget() { | |
| $(AUTH_TARGET).text(AUTH_TARGETS[selectedAuthType()] || ''); | |
| } | |
| function requestOtp() { | |
| if (lastSentAt > 0 && now() - lastSentAt < resendInterval) { | |
| startResendCoolDown(); | |
| return; | |
| } | |
| var authType = selectedAuthType(); | |
| $.post(PS_URL, {mode: 'sendOtp', authType: authType, authContext: AUTH_CONTEXT}, function (data) { | |
| if (data.success === false) { | |
| if ((data.data || {}).locked) { | |
| showLockedLayer(); | |
| return; | |
| } | |
| if ((data.data || {}).sendBlocked) { | |
| dialog_alert(data.message, '경고'); | |
| return; | |
| } | |
| showSendFail(data.message); | |
| return; | |
| } | |
| lastSentAt = now(); | |
| clearCodeError(); | |
| $(CODE_ROW).show(); | |
| $(CODE_INPUT).val('').focus(); | |
| $(REQUEST_BUTTON).text(RESEND_LABEL); | |
| startCountDown(); | |
| }, 'json'); | |
| } | |
| function showSendFail(message) { | |
| stopResendNotice(); | |
| $(RESEND_NOTICE).text(message); | |
| } | |
| function markCodeError(message) { | |
| $(CODE_FIELD).addClass('has-error'); | |
| $(CODE_NOTICE).text(message); | |
| } | |
| function clearCodeError() { | |
| $(CODE_FIELD).removeClass('has-error'); | |
| $(CODE_NOTICE).text(''); | |
| } | |
| function showExpiredNotice(message) { | |
| stopCountDown(); | |
| stopResendNotice(); | |
| lastSentAt = 0; | |
| $(COUNT_DOWN).text('0:00'); | |
| markCodeError(message); | |
| } | |
| function showVerifyFail(message, result) { | |
| if (result.resendRequired) { | |
| showExpiredNotice(message); | |
| return; | |
| } | |
| markCodeError(message); | |
| $(CODE_INPUT).focus(); | |
| } | |
| function completeAuth() { | |
| var code = $(CODE_INPUT).val(); | |
| if (code === '') { | |
| markCodeError('인증번호를 입력해 주세요.'); | |
| $(CODE_INPUT).focus(); | |
| return; | |
| } | |
| if (CODE_PATTERN.test(code) === false) { | |
| markCodeError('인증번호가 일치하지 않습니다.'); | |
| $(CODE_INPUT).focus(); | |
| return; | |
| } | |
| clearCodeError(); | |
| $.post(PS_URL, {mode: 'verifyOtp', code: code, authContext: AUTH_CONTEXT}, function (data) { | |
| if (data.success) { | |
| if (typeof window.occupancyAuthCompleted === 'function') { | |
| window.occupancyAuthCompleted(); | |
| } | |
| closeLayer(); | |
| return; | |
| } | |
| var result = data.data || {}; | |
| if (result.locked) { | |
| showLockedLayer(); | |
| return; | |
| } | |
| if (result.expired) { | |
| alert(data.message); | |
| closeLayer(); | |
| return; | |
| } | |
| showVerifyFail(data.message, result); | |
| }, 'json'); | |
| } | |
| if (window.occupancyAuthCleanup) { | |
| window.occupancyAuthCleanup(); | |
| } | |
| if ($(REQUEST_BUTTON).length > 0) { | |
| sessionHandle = setTimeout(function () { | |
| if (typeof window.occupancyAuthMarkExpired === 'function') { | |
| window.occupancyAuthMarkExpired(); | |
| } | |
| stopTimer(); | |
| // dialog_alert 는 [확인] 에서만 콜백을 실행해 [X]·ESC·바깥 클릭으로 닫으면 만료된 인증 모달이 남는다 | |
| BootstrapDialog.show({ | |
| title: '경고', | |
| message: '인증시간이 만료되었습니다. 다시 시도해주세요.', | |
| buttons: [{ | |
| label: '확인', | |
| cssClass: 'btn-black', | |
| hotkey: 13, | |
| action: function (dialog) { | |
| dialog.close(); | |
| } | |
| }], | |
| onhide: closeLayer | |
| }); | |
| }, popupSession * 1000); | |
| } | |
| window.occupancyAuthCleanup = stopTimer; | |
| $('.js-layer-close').on('click', stopTimer); | |
| $('input[name="occupancyAuthType"]').on('change', refreshAuthTarget); | |
| $('.js-request-occupancy-otp').on('click', requestOtp); | |
| $('.js-complete-occupancy-auth').on('click', completeAuth); | |
| })(); | |
| </script> | |
| <?php endif; ?> | |
변경 전layer_sms_contents.php
| <div> | |
| <div class="table-title gd-help-manual">SMS / LMS 문구 등록</div> | |
| <table class="table table-cols"> | |
| <colgroup> | |
| <col class="width-md"/> | |
| <tr class="text-center"> | |
| <td><input name="sno[]" type="checkbox" value="<?php echo $val['sno']; ?>" /></td> | |
| <td class="font-num"><?php echo number_format($page->idx--); ?></td> | |
| <td><?php echo $arrGroup[$val['smsAutoCode']]; ?></td> | |
| <td class="smsContents-<?php echo $val['sno']; ?>"><?php echo $val['contents']; ?></td> | |
| <td><input type="button" value="선택" class="btn btn-sm btn-black js-sms-contents-select" data-sno="<?php echo $val['sno']; ?>" /></span></td> | |
| </tr> | |
| // 글자수 체크 | |
| $('textarea[name=smsContentsText]').keyup(getSmsContentsLength).change(getSmsContentsLength); | |
| // SMS 문구 등록 | |
| $('.js-sms-contents-register').click(function (e) { | |
| var smsContentsGroup = $('select[name=\'smsContentsGroup\']').val(); | |
| var smsContentsText = $('textarea[name=\'smsContentsText\']').val(); | |
| if (smsContentsGroup == '' || smsContentsGroup == '0') { | |
| alert('그룹을 선택해 주세요.'); | |
| return; | |
| } | |
| if (smsContentsText == '') { | |
| alert('내용을 작성해 주세요.'); | |
| return; | |
| } | |
| if (data == 'OK') { | |
| layer_list_search('page=<?php echo $search['page'];?>'); | |
| } else { | |
| alert('등록시 오류가 발생하였습니다.'); | |
| } | |
| }); | |
| }); | |
| if (data == 'OK') { | |
| layer_list_search('page=<?php echo $search['page'];?>'); | |
| } else { | |
| alert('삭제시 오류가 발생하였습니다.'); | |
| } | |
| }); | |
| } | |
변경 후layer_sms_contents.php
| <script src="<?= gd_set_browser_cache(PATH_ADMIN_GD_SHARE . 'script/sms/sms-emoji.js') ?>"></script> | |
| <div> | |
| <div class="table-title gd-help-manual">SMS / LMS 문구 등록</div> | |
| <div class="notice-info mgb10"> | |
| 등록/수정한 문구는 승인 후 발송 가능합니다. 아래 목록에는 승인된 문구만 노출되며, 승인 처리는 회원 > SMS 관리 > SMS 템플릿 설정에서 가능합니다. | |
| </div> | |
| <table class="table table-cols"> | |
| <colgroup> | |
| <col class="width-md"/> | |
| <tr class="text-center"> | |
| <td><input name="sno[]" type="checkbox" value="<?php echo $val['sno']; ?>" /></td> | |
| <td class="font-num"><?php echo number_format($page->idx--); ?></td> | |
| <td><?= $arrGroup[$val['smsAutoCode']] ?? '' ?></td> | |
| <td class="smsContents-<?php echo $val['sno']; ?>"><?php echo $val['contents']; ?></td> | |
| <td><input type="button" value="선택" class="btn btn-sm btn-black js-sms-contents-select" data-sno="<?php echo $val['sno']; ?>" /></span></td> | |
| </tr> | |
| // 글자수 체크 | |
| $('textarea[name=smsContentsText]').keyup(getSmsContentsLength).change(getSmsContentsLength); | |
| smsEmoji.bind('textarea[name=smsContentsText]', getSmsContentsLength); | |
| // SMS 문구 등록 | |
| $('.js-sms-contents-register').click(function (e) { | |
| var smsContentsGroup = $('select[name=\'smsContentsGroup\']').val(); | |
| var smsContentsText = $('textarea[name=\'smsContentsText\']').val(); | |
| if (smsContentsGroup == '' || smsContentsGroup == '0') { | |
| dialog_alert('그룹을 선택해 주세요.', '정보'); | |
| return; | |
| } | |
| if (smsContentsText == '') { | |
| dialog_alert('내용을 작성해 주세요.', '정보'); | |
| return; | |
| } | |
| if (data == 'OK') { | |
| layer_list_search('page=<?php echo $search['page'];?>'); | |
| } else { | |
| dialog_alert(data && data != 'ERROR' ? data : '등록시 오류가 발생하였습니다.', '정보'); | |
| } | |
| }); | |
| }); | |
| if (data == 'OK') { | |
| layer_list_search('page=<?php echo $search['page'];?>'); | |
| } else { | |
| // 일부만 삭제돼도 안내 문구가 오고 dialog_alert 의 callback 은 [확인] 으로 닫을 때만 돌아, 닫는 방식과 무관하게 먼저 목록을 다시 읽는다 | |
| layer_list_search('page=<?php echo $search['page'];?>'); | |
| dialog_alert(data && data != 'ERROR' ? data : '삭제시 오류가 발생하였습니다.', '정보'); | |
| } | |
| }); | |
| } | |
변경 전AdminMenu.php
| * @return string 읽기: 'readonly' / 읽기+쓰기: 'writable' / 권한없음: '' | |
| */ | |
| public function getAccessMenuStatusByAdminMenuNo(string $adminMenuNo): string | |
| { | |
| $writableAll = false; | |
| $accessAll = false; | |
| $status = ''; | |
| $menuData = $this->getAdminMenuInfo($adminMenuNo); | |
| if ($menuData['adminMenuNo'] == '' || $menuData['adminMenuParentNo'] == '') { | |
| return $status; | |
변경 후AdminMenu.php
| * @return string 읽기: 'readonly' / 읽기+쓰기: 'writable' / 권한없음: '' | |
| */ | |
| public function getAccessMenuStatusByAdminMenuNo(string $adminMenuNo): string | |
| { | |
| return $this->getAccessMenuStatusByMenuData($this->getAdminMenuInfo($adminMenuNo)); | |
| } | |
| /** | |
| * @param array $adminMenuNos [es_adminMenu] 테이블의 adminMenuNo 목록 | |
| * | |
| * @return array adminMenuNo => 'writable' / 'readonly' / '' (존재하지 않는 메뉴는 키 없음) | |
| */ | |
| public function getAccessMenuStatusByAdminMenuNos(array $adminMenuNos): array | |
| { | |
| if (empty($adminMenuNos)) { | |
| return []; | |
| } | |
| $arrBind = []; | |
| foreach ($adminMenuNos as $adminMenuNo) { | |
| $this->db->bind_param_push($arrBind, 's', $adminMenuNo); | |
| } | |
| $this->db->strField = 'am.adminMenuNo, am.adminMenuParentNo'; | |
| $this->db->strWhere = 'am.adminMenuNo IN (' . implode(', ', array_fill(0, count($adminMenuNos), '?')) . ')'; | |
| $query = $this->db->query_complete(); | |
| $strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . DB_ADMIN_MENU . ' as am ' . implode(' ', $query); | |
| $menuList = $this->db->query_fetch($strSQL, $arrBind); | |
| $statusList = []; | |
| foreach ($menuList as $menuData) { | |
| $statusList[$menuData['adminMenuNo']] = $this->getAccessMenuStatusByMenuData($menuData); | |
| } | |
| return $statusList; | |
| } | |
| /** | |
| * @param array $menuData [es_adminMenu] 조회 행 (adminMenuNo, adminMenuParentNo 사용) | |
| * | |
| * @return string 읽기: 'readonly' / 읽기+쓰기: 'writable' / 권한없음: '' | |
| */ | |
| protected function getAccessMenuStatusByMenuData(array $menuData): string | |
| { | |
| $writableAll = false; | |
| $accessAll = false; | |
| $status = ''; | |
| if ($menuData['adminMenuNo'] == '' || $menuData['adminMenuParentNo'] == '') { | |
| return $status; | |
변경 전DBTableField.php
| // @formatter:off | |
| $arrField = [ | |
| ['val' => 'sno', 'typ' => 'i', 'def' => null], // 일련번호 | |
| ['val' => 'smsType', 'typ' => 's', 'def' => 'user'], // SMS 전송타입 (order, member, promotion, user) | |
| ['val' => 'smsAutoCode', 'typ' => 's', 'def' => null], // SMS 자동발송 코드 | |
| ['val' => 'smsAutoType', 'typ' => 's', 'def' => null], // SMS 자동발송 수신 종류 (member, admin, provider) | |
변경 후DBTableField.php
| // @formatter:off | |
| $arrField = [ | |
| ['val' => 'sno', 'typ' => 'i', 'def' => null], // 일련번호 | |
| ['val' => 'templateCode', 'typ' => 's', 'def' => null], | |
| ['val' => 'templateCategoryCode', 'typ' => 's', 'def' => null], | |
| ['val' => 'referenceSmsContentNo', 'typ' => 'i', 'def' => null], | |
| ['val' => 'approvalStatus', 'typ' => 's', 'def' => 'PENDING'], | |
| ['val' => 'autoConversionFl', 'typ' => 's', 'def' => 'n'], | |
| ['val' => 'smsType', 'typ' => 's', 'def' => 'user'], // SMS 전송타입 (order, member, promotion, user) | |
| ['val' => 'smsAutoCode', 'typ' => 's', 'def' => null], // SMS 자동발송 코드 | |
| ['val' => 'smsAutoType', 'typ' => 's', 'def' => null], // SMS 자동발송 수신 종류 (member, admin, provider) | |
변경 전Manager.php
| if ($sessionManager['managerId'] === $arrData['managerId']) { | |
| $sessionManager['isSmsAuth'] = $arrData['isSmsAuth']; | |
| $sessionManager['isEmailAuth'] = $arrData['isEmailAuth']; | |
| $sessionManager['email'] = $arrData['email']; | |
| $sessionManager['cellPhone'] = $arrData['cellPhone']; | |
| $session->set(self::SESSION_MANAGER_LOGIN, $sessionManager); | |
| } | |
| if ($smsRoleGuard->isDelegateOrHigher() === false) { | |
| throw new Exception(__('SMS 발송 권한은 발신관리책임자 또는 발신관리위임자만 부여할 수 있습니다.<br>해당 권한을 해제한 후 다시 저장해 주세요.')); | |
| } | |
| if ($smsRoleGuard->isRepresentative() && $smsRoleService->isBlockedRepresentative()) { | |
| throw new Exception(__('SMS 발송 메뉴 접근이 제한되었습니다.<br>스팸 또는 부정 사용으로 차단되어 접근할 수 없습니다.<br>자세한 사항은 1:1문의로 문의하시기 바랍니다.')); | |
| } | |
| */ | |
| protected function setAuthenticationInfo(&$arrData, $smsAuth, $emailAuth) | |
| { | |
| if ($smsAuth['isAdminSmsAuth'] === true) { | |
| if (str_replace('-', '', $smsAuth['authCellPhone']) !== str_replace('-', '', $arrData['cellPhone'])) { | |
| throw new ManagerAuthException(ManagerAuthException::CODE_NOT_EQUALS_CELLPHONE); | |
| } | |
| $arrData['isSmsAuth'] = 'y'; | |
| $adminSecuritySmsAuthNumber = Otp::getOtp(8); | |
| $contents = sprintf(__('관리자님 인증번호는 [%s] 입니다.'), $adminSecuritySmsAuthNumber); | |
| $isSent = \App::getInstance(OccupancyOtpSmsService::class)->send($managerInfo['cellPhone'], $contents, $adminSecuritySmsAuthNumber); | |
| $smsAuth['smsAuthNumber'] = $adminSecuritySmsAuthNumber; | |
| $smsAuth['message'] = $isSent ? 'OK' : 'SMS Send Fail'; | |
변경 후Manager.php
| if ($sessionManager['managerId'] === $arrData['managerId']) { | |
| $sessionManager['isSmsAuth'] = $arrData['isSmsAuth']; | |
| $sessionManager['isEmailAuth'] = $arrData['isEmailAuth']; | |
| if (array_key_exists('email', $arrData)) { | |
| $sessionManager['email'] = $arrData['email']; | |
| } | |
| if (array_key_exists('cellPhone', $arrData)) { | |
| $sessionManager['cellPhone'] = $arrData['cellPhone']; | |
| } | |
| $session->set(self::SESSION_MANAGER_LOGIN, $sessionManager); | |
| } | |
| if ($smsRoleGuard->isDelegateOrHigher() === false) { | |
| throw new Exception(__('SMS 발송 권한은 발신관리책임자 또는 발신관리위임자만 부여할 수 있습니다.<br>해당 권한을 해제한 후 다시 저장해 주세요.')); | |
| } | |
| if ($smsRoleService->isUncertifiedDelegateActor()) { | |
| throw new Exception(__('SMS 발송 권한은 발신관리책임자 또는 발신관리위임자만 부여할 수 있습니다.<br>본인인증을 완료해야 발신관리위임자 역할을 수행할 수 있습니다.<br>운영자 수정 페이지에서 본인인증을 먼저 완료하시기 바랍니다.')); | |
| } | |
| if ($smsRoleGuard->isRepresentative() && $smsRoleService->isBlockedRepresentative()) { | |
| throw new Exception(__('SMS 발송 메뉴 접근이 제한되었습니다.<br>스팸 또는 부정 사용으로 차단되어 접근할 수 없습니다.<br>자세한 사항은 1:1문의로 문의하시기 바랍니다.')); | |
| } | |
| */ | |
| protected function setAuthenticationInfo(&$arrData, $smsAuth, $emailAuth) | |
| { | |
| $isAuthCellPhoneReplaced = $smsAuth['isAdminSmsAuth'] === true | |
| && str_replace('-', '', $smsAuth['authCellPhone']) !== str_replace('-', '', $arrData['cellPhone']); | |
| // 수정 저장에서 점유인증 뒤 번호를 다시 고치면 미인증으로 보고 기존 번호를 유지한다 | |
| if ($isAuthCellPhoneReplaced && ($arrData['mode'] ?? '') === 'modify') { | |
| $smsAuth['isAdminSmsAuth'] = false; | |
| } | |
| if ($smsAuth['isAdminSmsAuth'] === true) { | |
| if ($isAuthCellPhoneReplaced) { | |
| throw new ManagerAuthException(ManagerAuthException::CODE_NOT_EQUALS_CELLPHONE); | |
| } | |
| $arrData['isSmsAuth'] = 'y'; | |
| $adminSecuritySmsAuthNumber = Otp::getOtp(8); | |
| $contents = sprintf(__('관리자님 인증번호는 [%s] 입니다.'), $adminSecuritySmsAuthNumber); | |
| $isSent = \App::getInstance(OccupancyOtpSmsService::class)->send($managerInfo['cellPhone'], $contents); | |
| $smsAuth['smsAuthNumber'] = $adminSecuritySmsAuthNumber; | |
| $smsAuth['message'] = $isSent ? 'OK' : 'SMS Send Fail'; | |
변경 전SmsAdmin.php
| use Origin\Service\Member\CrmGroupService; | |
| use Origin\Enum\ApiClient\Notification\SmsPersonalInfoAccessLogEventType; | |
| use Origin\Enum\ManagerTutorialType; | |
| use Origin\Service\Admin\Member\ManagerTutorial\ManagerTutorialTopicService; | |
| use Origin\Service\Crm\Message\SmsPersonalInfoAccessLogService; | |
| /** | |
| * SMS 관리자 관련 처리 | |
| // CLI(스케줄러) 세션은 SessionManager 가 아니라 발행 서비스 생성이 매번 실패하고 수신대상 루프 안에서 호출돼, 첫 실패 후에는 같은 프로세스에서 재시도·재로그하지 않는다 | |
| protected static $personalInfoAccessLogPublishFailed = false; | |
| const ALPHA_SMS_POINT_LIST = 'https://alpha-www.nhn-commerce.com/userinterface/_godoConn/conf/sms.cfg'; | |
| const REAL_SMS_POINT_LIST = 'https://www.nhn-commerce.com/userinterface/_godoConn/conf/sms.cfg'; | |
| */ | |
| public function saveSmsAuto($request) | |
| { | |
| // 디비에 저장될 항목을 정리 (insert , update) | |
| $smsAutoReceiveKind = Sms::SMS_AUTO_RECEIVE_LIST; | |
| $smsAutoType = [ | |
| 'member', | |
| 'admin', | |
| 'provider', | |
| 'board', | |
| ]; | |
| foreach ($smsAutoReceiveKind as $smsType => $val) { | |
| foreach ($request[$smsType] as $autoCode => $autoData) { | |
| foreach ($smsAutoType as $autoType) { | |
| if (isset($autoData[$autoType . 'Mode'])) { | |
| $dbData[$autoData[$autoType . 'Mode']][$smsType][$autoCode][$autoType]['sno'] = $autoData[$autoType . 'Sno']; | |
| $dbData[$autoData[$autoType . 'Mode']][$smsType][$autoCode][$autoType]['subject'] = $autoData['subject']; | |
| $dbData[$autoData[$autoType . 'Mode']][$smsType][$autoCode][$autoType]['contents'] = $autoData[$autoType . 'Contents']; | |
| $dbData[$autoData[$autoType . 'Mode']][$smsType][$autoCode][$autoType]['messageTitle'] = gd_isset($autoData[$autoType . 'MessageTitle'], ''); | |
| // $dbData[$autoData[$autoType . 'Mode']][$smsType][$autoCode][$autoType]['smsType'] = $smsType; | |
| unset($request[$smsType][$autoCode][$autoType . 'Mode']); | |
| unset($request[$smsType][$autoCode][$autoType . 'Sno']); | |
| unset($request[$smsType][$autoCode]['subject']); | |
| unset($request[$smsType][$autoCode][$autoType . 'Contents']); | |
| unset($request[$smsType][$autoCode][$autoType . 'MessageTitle']); | |
| } | |
| } | |
| } | |
| } | |
| // 디비 정보 저장 | |
| foreach ($dbData as $saveMode => $tmpData1) { | |
| foreach ($tmpData1 as $smsType => $tmpData2) { | |
| foreach ($tmpData2 as $smsAutoCode => $tmpData3) { | |
| foreach ($tmpData3 as $smsAutoType => $saveData) { | |
| $arrBind = []; | |
| // 저장 방법 | |
| if ($saveMode === 'insert') { | |
| $saveData['groupNm'] = __('자동발송'); | |
| $saveData['smsAutoCode'] = $smsAutoCode; | |
| $saveData['smsAutoType'] = $smsAutoType; | |
| $saveData['smsType'] = $smsType; | |
| $arrBind = $this->db->get_binding(DBTableField::tableSmsContents(), $saveData, 'insert'); | |
| $this->db->set_insert_db(DB_SMS_CONTENTS, $arrBind['param'], $arrBind['bind'], 'y'); | |
| } elseif ($saveMode === 'update') { | |
| $arrBind = $this->db->get_binding(DBTableField::tableSmsContents(), $saveData, 'update', array_keys($saveData)); | |
| $this->db->bind_param_push($arrBind['bind'], 'i', $saveData['sno']); | |
| $this->db->set_update_db(DB_SMS_CONTENTS, $arrBind['param'], 'sno=?', $arrBind['bind']); | |
| } | |
| unset($arrBind); | |
| } | |
| } | |
| } | |
| } | |
| // 기 저장된 항목 삭제 | |
| unset($request['mode'], $request['smsCallNum']); | |
| gd_set_policy('sms.smsAuto', $request); | |
| } | |
| /** | |
| * SMS 자동 발송 관련 설정 저장 | |
| * | |
| $this->db->strField = implode(', ', DBTableField::setTableField('tableSmsContents')); | |
| // $this->db->strWhere = 'smsType IN (\'order\', \'member\', \'promotion\', \'board\', \'admin\') AND smsAutoCode IS NOT NULL AND smsAutoType IS NOT NULL';// 2016-08-02 무통장입금 SMS 들어가면서 admin 메뉴 임시로 제거함 다른 부분이 들어갈꺼면 처리하면 됨 | |
| $this->db->strWhere = 'smsType IN (\'order\', \'member\', \'promotion\', \'board\') AND smsAutoCode IS NOT NULL AND smsAutoType IS NOT NULL'; | |
| $this->db->strOrder = 'smsType ASC'; | |
| $query = $this->db->query_complete(); | |
| $strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . DB_SMS_CONTENTS . array_shift($query) . ' ' . implode(' ', $query); | |
| $getData = gd_htmlspecialchars_stripslashes($this->db->query_fetch($strSQL, null, false)); | |
| foreach ($getData as $data) { | |
| $setData[$data['smsType']][$data['smsAutoCode']][$data['smsAutoType']]['contents'] = $data['contents']; | |
| $setData[$data['smsType']][$data['smsAutoCode']][$data['smsAutoType']]['messageTitle'] = $data['messageTitle']; | |
| $setData[$data['smsType']][$data['smsAutoCode']][$data['smsAutoType']]['sno'] = $data['sno']; | |
| } | |
| unset($getData); | |
| // SMS 자동발송 설정값 불러오기 | |
| $smsAutoType[$keyType][$key][$sendType . 'MessageTitle'] = gd_isset($setData[$keyType][$val['code']][$sendType]['messageTitle'], ''); | |
| $smsAutoType[$keyType][$key][$sendType . 'Mode'] = 'update'; | |
| $smsAutoType[$keyType][$key][$sendType . 'Sno'] = $setData[$keyType][$val['code']][$sendType]['sno']; | |
| } else { | |
| $smsAutoType[$keyType][$key][$sendType . 'Contents'] = $val[$sendType . 'Contents']; | |
| $smsAutoType[$keyType][$key][$sendType . 'MessageTitle'] = ''; | |
| $smsAutoType[$keyType][$key][$sendType . 'Mode'] = 'insert'; | |
| $smsAutoType[$keyType][$key][$sendType . 'Sno'] = ''; | |
| } | |
| } | |
| // 기본 검색 | |
| $arrWhere[] = 'smsType = ?'; | |
| $this->db->bind_param_push($arrBind, 's', $smsType); | |
| // 키워드 검색 | |
| if ($search['key'] && $search['keyword']) { | |
| * @param array $postValue SMS 문구 | |
| * | |
| * @return bool | |
| */ | |
| public function saveSmsContentsData(array $postValue) | |
| { | |
| } | |
| $postValue['smsType'] = 'user'; | |
| StringUtils::strIsSet($postValue['subject'], ''); | |
| if (key_exists('sno', $postValue)) { | |
| $arrBind = $this->db->get_binding(DBTableField::tableSmsContents(), $postValue, 'update'); | |
| $this->db->bind_param_push($arrBind['bind'], 'i', $postValue['sno']); | |
| $this->db->set_update_db(DB_SMS_CONTENTS, $arrBind['param'], 'sno = ?', $arrBind['bind']); | |
| } else { | |
| $arrBind = $this->db->get_binding(DBTableField::tableSmsContents(), $postValue, 'insert'); | |
| $this->db->set_insert_db(DB_SMS_CONTENTS, $arrBind['param'], $arrBind['bind'], 'y'); | |
| } | |
| return true; | |
| } elseif (!$code->existsCode($storage->get('smsAutoCode'))) { | |
| $errors[] = '존재하지 않는 그룹입니다.'; | |
| } | |
| if (!Validator::stringType()->length(1, 10)->validate($storage->get('subject'))) { | |
| $errors[] = '제목을 입력하여 주세요.'; | |
| } | |
| if (!Validator::stringType()->length(1)->validate($storage->get('contents'))) { | |
| $errors[] = '내용을 입력하여 주세요.'; | |
| } | |
| if (StringUtils::strLength($storage->get('contents')) > 2000) { | |
| $errors[] = '내용은 최대 2000 bytes 까지 입력 가능합니다.'; | |
| } | |
| if (count($errors) > 0) { | |
| throw new \Exception(implode('\r\n', $errors)); | |
| } | |
변경 후SmsAdmin.php
| use Origin\Service\Member\CrmGroupService; | |
| use Origin\Enum\ApiClient\Notification\SmsPersonalInfoAccessLogEventType; | |
| use Origin\Enum\ManagerTutorialType; | |
| use Origin\Repository\Sms\SmsContentsRepository; | |
| use Origin\Enum\Sms\Template\SmsTemplateApprovalStatus; | |
| use Origin\Enum\Sms\Template\SmsTemplateCategory; | |
| use Origin\Service\Admin\Member\ManagerTutorial\ManagerTutorialTopicService; | |
| use Origin\Service\Crm\Message\SmsPersonalInfoAccessLogService; | |
| use Origin\Service\Sms\AutoSend\SaveAutoSendContentsService; | |
| use Origin\Service\Sms\Template\SmsTemplateService; | |
| /** | |
| * SMS 관리자 관련 처리 | |
| // CLI(스케줄러) 세션은 SessionManager 가 아니라 발행 서비스 생성이 매번 실패하고 수신대상 루프 안에서 호출돼, 첫 실패 후에는 같은 프로세스에서 재시도·재로그하지 않는다 | |
| protected static $personalInfoAccessLogPublishFailed = false; | |
| const SMS_AUTO_TYPES = [ | |
| 'member', | |
| 'admin', | |
| 'provider', | |
| 'board', | |
| ]; | |
| const ALPHA_SMS_POINT_LIST = 'https://alpha-www.nhn-commerce.com/userinterface/_godoConn/conf/sms.cfg'; | |
| const REAL_SMS_POINT_LIST = 'https://www.nhn-commerce.com/userinterface/_godoConn/conf/sms.cfg'; | |
| */ | |
| public function saveSmsAuto($request) | |
| { | |
| /** @var SaveAutoSendContentsService $saveAutoSendContentsService */ | |
| $saveAutoSendContentsService = \App::getInstance(SaveAutoSendContentsService::class); | |
| $request = $saveAutoSendContentsService->saveAutoSendContents( | |
| $request, | |
| array_keys(Sms::SMS_AUTO_RECEIVE_LIST), | |
| self::SMS_AUTO_TYPES | |
| ); | |
| // 기 저장된 항목 삭제 | |
| unset($request['mode'], $request['smsCallNum']); | |
| gd_set_policy('sms.smsAuto', $request); | |
| } | |
| /** | |
| * @return SmsTemplateService | |
| */ | |
| protected function getSmsTemplateService(): SmsTemplateService | |
| { | |
| return \App::getInstance(SmsTemplateService::class); | |
| } | |
| /** | |
| * SMS 자동 발송 관련 설정 저장 | |
| * | |
| $this->db->strField = implode(', ', DBTableField::setTableField('tableSmsContents')); | |
| // $this->db->strWhere = 'smsType IN (\'order\', \'member\', \'promotion\', \'board\', \'admin\') AND smsAutoCode IS NOT NULL AND smsAutoType IS NOT NULL';// 2016-08-02 무통장입금 SMS 들어가면서 admin 메뉴 임시로 제거함 다른 부분이 들어갈꺼면 처리하면 됨 | |
| $this->db->strWhere = 'smsType IN (\'order\', \'member\', \'promotion\', \'board\') AND smsAutoCode IS NOT NULL AND smsAutoType IS NOT NULL'; | |
| $this->db->strOrder = 'smsType ASC, sno ASC'; | |
| $query = $this->db->query_complete(); | |
| $strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . DB_SMS_CONTENTS . array_shift($query) . ' ' . implode(' ', $query); | |
| $getData = gd_htmlspecialchars_stripslashes($this->db->query_fetch($strSQL, null, false)); | |
| foreach ($getData as $data) { | |
| if ((int) ($data['referenceSmsContentNo'] ?? 0) === SmsContentsRepository::CLEARED_REFERENCE_SMS_CONTENT_NO) { | |
| $data['contents'] = ''; | |
| $data['messageTitle'] = ''; | |
| } | |
| $setData[$data['smsType']][$data['smsAutoCode']][$data['smsAutoType']]['contents'] = $data['contents']; | |
| $setData[$data['smsType']][$data['smsAutoCode']][$data['smsAutoType']]['messageTitle'] = $data['messageTitle']; | |
| $setData[$data['smsType']][$data['smsAutoCode']][$data['smsAutoType']]['sno'] = $data['sno']; | |
| $setData[$data['smsType']][$data['smsAutoCode']][$data['smsAutoType']]['templateCategoryCode'] = $data['templateCategoryCode']; | |
| $setData[$data['smsType']][$data['smsAutoCode']][$data['smsAutoType']]['referenceSmsContentNo'] = $data['referenceSmsContentNo'] ?? null; | |
| } | |
| unset($getData); | |
| // SMS 자동발송 설정값 불러오기 | |
| $smsAutoType[$keyType][$key][$sendType . 'MessageTitle'] = gd_isset($setData[$keyType][$val['code']][$sendType]['messageTitle'], ''); | |
| $smsAutoType[$keyType][$key][$sendType . 'Mode'] = 'update'; | |
| $smsAutoType[$keyType][$key][$sendType . 'Sno'] = $setData[$keyType][$val['code']][$sendType]['sno']; | |
| $smsAutoType[$keyType][$key][$sendType . 'TemplateCategoryCode'] = gd_isset($setData[$keyType][$val['code']][$sendType]['templateCategoryCode'], ''); | |
| $smsAutoType[$keyType][$key][$sendType . 'ReferenceSmsContentNo'] = (int) gd_isset($setData[$keyType][$val['code']][$sendType]['referenceSmsContentNo'], 0); | |
| } else { | |
| $smsAutoType[$keyType][$key][$sendType . 'Contents'] = $val[$sendType . 'Contents']; | |
| $smsAutoType[$keyType][$key][$sendType . 'MessageTitle'] = ''; | |
| $smsAutoType[$keyType][$key][$sendType . 'Mode'] = 'insert'; | |
| $smsAutoType[$keyType][$key][$sendType . 'Sno'] = ''; | |
| $smsAutoType[$keyType][$key][$sendType . 'TemplateCategoryCode'] = ''; | |
| $smsAutoType[$keyType][$key][$sendType . 'ReferenceSmsContentNo'] = 0; | |
| } | |
| } | |
| // 기본 검색 | |
| $arrWhere[] = 'smsType = ?'; | |
| $this->db->bind_param_push($arrBind, 's', $smsType); | |
| $arrWhere[] = 'approvalStatus = ?'; | |
| $this->db->bind_param_push($arrBind, 's', SmsTemplateApprovalStatus::APPROVED); | |
| // 키워드 검색 | |
| if ($search['key'] && $search['keyword']) { | |
| * @param array $postValue SMS 문구 | |
| * | |
| * @return bool | |
| * @throws \Origin\Exception\Sms\Template\SmsTemplateException 권한이 없거나 수정 불가 상태이거나 입력값이 유효하지 않은 경우 | |
| */ | |
| public function saveSmsContentsData(array $postValue) | |
| { | |
| } | |
| $postValue['smsType'] = 'user'; | |
| StringUtils::strIsSet($postValue['subject'], ''); | |
| $hasSno = key_exists('sno', $postValue); | |
| $smsTemplateService = $this->getSmsTemplateService(); | |
| $postValue = $smsTemplateService->normalizeSendText($postValue, $hasSno ? (int) $postValue['sno'] : null); | |
| $contents = $postValue['contents']; | |
| $smsTemplateService->assertSavable($postValue, $hasSno ? (int) $postValue['sno'] : null); | |
| if ($hasSno) { | |
| $messageTitle = isset($postValue['messageTitle']) ? (string) $postValue['messageTitle'] : null; | |
| $modifyResult = $smsTemplateService->resolveModifyResult( | |
| (int) $postValue['sno'], | |
| $contents, | |
| isset($postValue['subject']) ? (string) $postValue['subject'] : null, | |
| $messageTitle | |
| ); | |
| if ($modifyResult['status'] !== null) { | |
| $postValue['approvalStatus'] = $modifyResult['status']; | |
| } | |
| if ($modifyResult['contentsChanged'] === false) { | |
| unset($postValue['contents']); | |
| } | |
| if (!$modifyResult['messageTitleChanged']) { | |
| unset($postValue['messageTitle']); | |
| } | |
| $arrBind = $this->db->get_binding(DBTableField::tableSmsContents(), $postValue, 'update', array_keys($postValue)); | |
| $this->db->bind_param_push($arrBind['bind'], 'i', $postValue['sno']); | |
| $this->db->set_update_db(DB_SMS_CONTENTS, $arrBind['param'], 'sno = ?', $arrBind['bind']); | |
| if ($modifyResult['modified']) { | |
| // UPDATE 에서 뺀 제목·내용은 DB 원본이 그대로 남으므로 이력도 원본으로 맞춘다 | |
| $smsTemplateService->writeModifiedHistorySafely( | |
| (int) $postValue['sno'], | |
| (string) $postValue['subject'], | |
| $modifyResult['contentsChanged'] ? $contents : $modifyResult['storedContents'], | |
| $messageTitle !== null && !$modifyResult['messageTitleChanged'] ? $modifyResult['storedMessageTitle'] : $messageTitle | |
| ); | |
| } | |
| } else { | |
| $categoryCode = SmsTemplateCategory::getCategoryCode(SmsTemplateCategory::fromSmsAutoCode($postValue['smsAutoCode'])); | |
| if (empty($postValue['templateCategoryCode']) && $categoryCode !== '') { | |
| $postValue['templateCategoryCode'] = $categoryCode; | |
| } | |
| $postValue['approvalStatus'] = SmsTemplateApprovalStatus::PENDING; | |
| $arrBind = $this->db->get_binding(DBTableField::tableSmsContents(), $postValue, 'insert'); | |
| $this->db->set_insert_db(DB_SMS_CONTENTS, $arrBind['param'], $arrBind['bind'], 'y'); | |
| $smsTemplateService->issueTemplateCodeWithHistory( | |
| (int) $this->db->insert_id(), | |
| (string) $postValue['subject'], | |
| (string) $postValue['contents'], | |
| SmsTemplateApprovalStatus::PENDING, | |
| isset($postValue['messageTitle']) ? (string) $postValue['messageTitle'] : null | |
| ); | |
| } | |
| return true; | |
| } elseif (!$code->existsCode($storage->get('smsAutoCode'))) { | |
| $errors[] = '존재하지 않는 그룹입니다.'; | |
| } | |
| if (count($errors) > 0) { | |
| throw new \Exception(implode('\r\n', $errors)); | |
| } | |
변경 전SmsAuto.php
| use Framework\Utility\ComponentUtils; | |
| use Framework\Utility\StringUtils; | |
| use Origin\Enum\ApiClient\Notification\SmsPersonalInfoAccessLogEventType; | |
| use Origin\Service\Crm\Message\SmsPersonalInfoAccessLogService; | |
| use Origin\Service\Member\KakaoAlrim\KakaoAlrimCloudSendService; | |
| use SplObserver; | |
| { | |
| $logger = \App::getInstance('logger'); | |
| $binds = []; | |
| $this->db->strField = 'smsType, smsAutoType, contents, subject, messageTitle'; | |
| $this->db->strJoin = DB_SMS_CONTENTS; | |
| $this->db->strWhere = 'smsType= ? AND smsAutoCode = ? AND smsAutoType IN (\'' . implode('\', \'', $wheres) . '\') AND contents != \'\' AND contents IS NOT NULL'; | |
| $this->db->bind_param_push($binds, 's', $this->smsType); | |
| $this->db->bind_param_push($binds, 's', $this->smsAutoCodeType); | |
| $query = $this->db->query_complete(); | |
| $strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . array_shift($query) . ' ' . implode(' ', $query); | |
| $getData = $this->db->query_fetch($strSQL, $binds); | |
| throw new \Exception(__('자동발송 내용을 찾을 수 없습니다.')); | |
| } | |
| $getData = StringUtils::htmlSpecialCharsStripSlashes($getData); | |
| return $getData; | |
| } | |
| /** | |
변경 후SmsAuto.php
| use Framework\Utility\ComponentUtils; | |
| use Framework\Utility\StringUtils; | |
| use Origin\Enum\ApiClient\Notification\SmsPersonalInfoAccessLogEventType; | |
| use Origin\Enum\Sms\Template\SmsTemplateApprovalStatus; | |
| use Origin\Service\Crm\Message\SmsPersonalInfoAccessLogService; | |
| use Origin\Service\Member\KakaoAlrim\KakaoAlrimCloudSendService; | |
| use SplObserver; | |
| { | |
| $logger = \App::getInstance('logger'); | |
| $binds = []; | |
| $approvedStatus = SmsTemplateApprovalStatus::APPROVED; | |
| $latestApprovedOf = function ($column) { | |
| return 'SELECT stah.' . $column | |
| . ' FROM ' . DB_SMS_TEMPLATE_APPROVAL_HISTORY . ' stah' | |
| . ' WHERE stah.smsContentNo = rsc.sno' | |
| . ' AND stah.approvalStatus = ?' | |
| . ' ORDER BY stah.historyNo DESC LIMIT 1'; | |
| }; | |
| $latestApproved = $latestApprovedOf('templateContent'); | |
| $latestApprovedTitle = $latestApprovedOf('messageTitle'); | |
| $isApproved = 'rsc.approvalStatus = ?'; | |
| $field = []; | |
| $field[] = 'sc.smsType'; | |
| $field[] = 'sc.smsAutoType'; | |
| $field[] = 'IF(' . $isApproved . ', rsc.contents, (' . $latestApproved . ')) AS contents'; | |
| $field[] = 'sc.subject'; | |
| $field[] = 'IF(' . $isApproved . ', rsc.messageTitle, COALESCE((' . $latestApprovedTitle . '), rsc.messageTitle)) AS messageTitle'; | |
| $where = []; | |
| $where[] = 'sc.smsType = ?'; | |
| $where[] = 'sc.smsAutoCode = ?'; | |
| $where[] = 'sc.smsAutoType IN (\'' . implode('\', \'', $wheres) . '\')'; | |
| $where[] = 'rsc.contents != \'\''; | |
| $where[] = 'rsc.contents IS NOT NULL'; | |
| $where[] = '(' . $isApproved . ' OR (' . $latestApproved . ') IS NOT NULL)'; | |
| $this->db->strField = implode(', ', $field); | |
| $this->db->strJoin = DB_SMS_CONTENTS . ' sc INNER JOIN ' . DB_SMS_CONTENTS . ' rsc ON rsc.sno = COALESCE(sc.referenceSmsContentNo, sc.sno)'; | |
| $this->db->strWhere = implode(' AND ', $where); | |
| $this->db->strOrder = 'sc.sno ASC'; | |
| // query_complete() 가 SELECT → FROM → WHERE 순으로 조립하므로 바인드도 그 순서로 밀어 넣는다 | |
| $this->db->bind_param_push($binds, 's', $approvedStatus); | |
| $this->db->bind_param_push($binds, 's', $approvedStatus); | |
| $this->db->bind_param_push($binds, 's', $approvedStatus); | |
| $this->db->bind_param_push($binds, 's', $approvedStatus); | |
| $this->db->bind_param_push($binds, 's', $this->smsType); | |
| $this->db->bind_param_push($binds, 's', $this->smsAutoCodeType); | |
| $this->db->bind_param_push($binds, 's', $approvedStatus); | |
| $this->db->bind_param_push($binds, 's', $approvedStatus); | |
| $query = $this->db->query_complete(); | |
| $strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . array_shift($query) . ' ' . implode(' ', $query); | |
| $getData = $this->db->query_fetch($strSQL, $binds); | |
| throw new \Exception(__('자동발송 내용을 찾을 수 없습니다.')); | |
| } | |
| $getData = StringUtils::htmlSpecialCharsStripSlashes($getData); | |
| $latestByAutoType = []; | |
| foreach ($getData as $row) { | |
| $latestByAutoType[$row['smsAutoType']] = $row; | |
| } | |
| return array_values($latestByAutoType); | |
| } | |
| /** | |
변경 전SmsAutoController.php
| use Framework\Utility\ComponentUtils; | |
| use Framework\Utility\ConfigUrlUtils; | |
| use Origin\Service\Crm\Message\SmsManageCallNumberService; | |
| /** | |
| * SMS 자동 발송 설정 | |
| $this->setData('smsAutoReSendTime', Sms::SMS_RE_SEND_TIME); | |
| $this->setData('smsAutoCouponLimitPeriod', Sms::SMS_COUPON_LIMIT_PERIOD); | |
| $this->setData('smsAutoReservationTime', $smsAdmin->getSmsReservationTime()); | |
| $this->setData('smsAutoData', gd_htmlspecialchars($smsAutoData)); | |
| $this->setData('checked', gd_isset($checked)); | |
| $this->setData('hasRegisteredCallNumber', $hasRegisteredCallNumber); | |
변경 후SmsAutoController.php
| use Framework\Utility\ComponentUtils; | |
| use Framework\Utility\ConfigUrlUtils; | |
| use Origin\Service\Crm\Message\SmsManageCallNumberService; | |
| use Origin\Service\Sms\Template\SmsTemplateService; | |
| /** | |
| * SMS 자동 발송 설정 | |
| $this->setData('smsAutoReSendTime', Sms::SMS_RE_SEND_TIME); | |
| $this->setData('smsAutoCouponLimitPeriod', Sms::SMS_COUPON_LIMIT_PERIOD); | |
| $this->setData('smsAutoReservationTime', $smsAdmin->getSmsReservationTime()); | |
| /** @var SmsTemplateService $smsTemplateService */ | |
| $smsTemplateService = \App::getInstance(SmsTemplateService::class); | |
| $this->setData('smsAutoTemplateSelections', $smsTemplateService->buildAutoSendSelections($smsAutoData, array_keys(Sms::SMS_AUTO_RECEIVE_LIST))); | |
| $this->setData('smsAutoTemplateContents', $smsTemplateService->getApprovedTemplateContentsBySno()); | |
| $this->setData('smsAutoTemplateLmsTitles', $smsTemplateService->getApprovedTemplateLmsTitlesBySno()); | |
| $this->setData('smsAutoData', gd_htmlspecialchars($smsAutoData)); | |
| $this->setData('checked', gd_isset($checked)); | |
| $this->setData('hasRegisteredCallNumber', $hasRegisteredCallNumber); | |
변경 전SmsSendController.php
| namespace Bundle\Controller\Admin\Member; | |
| use Component\Code\Code; | |
| use Component\Member\Group\Util; | |
| use Component\Member\MemberDAO; | |
| use Component\Sms\Sms; | |
| use Framework\Utility\DateTimeUtils; | |
| use Framework\Utility\GodoUtils; | |
| use Framework\Utility\StringUtils; | |
| use Origin\Service\Crm\Message\SmsManageCallNumberService; | |
| /** | |
| * SMS 개별/전체 발송 | |
| */ | |
| class SmsSendController extends \Controller\Admin\Controller | |
| { | |
| /** | |
| * @inheritdoc | |
| */ | |
| } elseif ($opener === 'goods') { | |
| //상품 재입고 알림 | |
| $this->setData('receiverCount', count($request->get()->get('receiverKeys', []))); | |
| $getSmsContentsByGoodsRestock = function () { | |
| $returnData = ''; | |
| $db = \App::getInstance('DB'); | |
| $db->strField = '*'; | |
| $db->strWhere = 'smsType = \'user\' AND smsAutoCode LIKE CONCAT(\'01007501\', \'%\')'; | |
| $query = $db->query_complete(); | |
| $strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . DB_SMS_CONTENTS . ' ' . implode(' ', $query); | |
| list($resultData) = $db->query_fetch($strSQL); | |
| $returnData = StringUtils::htmlSpecialCharsStripSlashes($resultData['contents']); | |
| return $returnData; | |
| }; | |
| $smsContents = $getSmsContentsByGoodsRestock(); | |
| } | |
| } else if ($request->request()->has('receiverMemNo') || ($request->request()->has('receiverNm') && $request->request()->has('receiverPhone'))) { | |
| $popupMode = true; | |
| // SMS 포인트 Sync | |
| Sms::saveSmsPoint(); | |
| $code = new Code(); | |
| /** | |
| * set view data | |
| */ | |
| $this->setData('reSenderData', $reSenderData); | |
| $this->setData('receiveTotal', $receiveTotal); | |
| $this->setData('rejectCount', $rejectCount); | |
| $this->setData('smsContentsGroupCode', $code->getGroupItems('01007')); | |
| $this->setData('smsAutoCode', $popupMode ? 'order' : ''); | |
| $this->setData('replaceCodeGroupKey', $popupMode ? '' : 'member'); | |
| $this->setData('replaceCodeGroup', $replaceCodeGroup); | |
| // 엑셀 업로드 사용가능 여부 확인 | |
| $excelUploadUse = ComponentUtils::getPolicy('sms.config')['excelUploadUse']; | |
| return false; | |
| } | |
| } | |
| } | |
변경 후SmsSendController.php
| namespace Bundle\Controller\Admin\Member; | |
| use Component\Member\Group\Util; | |
| use Component\Member\MemberDAO; | |
| use Component\Sms\Sms; | |
| use Framework\Utility\DateTimeUtils; | |
| use Framework\Utility\GodoUtils; | |
| use Framework\Utility\StringUtils; | |
| use Origin\Constants\Sms\SmsReplaceCode; | |
| use Origin\Enum\Sms\Template\SmsTemplateCategory; | |
| use Origin\Service\Crm\Message\SmsManageCallNumberService; | |
| use Origin\Service\Sms\Template\SmsTemplateService; | |
| /** | |
| * SMS 개별/전체 발송 | |
| */ | |
| class SmsSendController extends \Controller\Admin\Controller | |
| { | |
| const GOODS_RESTOCK_AUTO_CODE = '01007501'; | |
| const TEMPLATE_CATEGORY_CODE_GROUP = '01007'; | |
| /** | |
| * @inheritdoc | |
| */ | |
| } elseif ($opener === 'goods') { | |
| //상품 재입고 알림 | |
| $this->setData('receiverCount', count($request->get()->get('receiverKeys', []))); | |
| /** @var SmsTemplateService $smsTemplateService */ | |
| $smsTemplateService = \App::getInstance(SmsTemplateService::class); | |
| $smsContents = StringUtils::htmlSpecialCharsStripSlashes( | |
| $smsTemplateService->getApprovedAutoSendContents(self::GOODS_RESTOCK_AUTO_CODE) | |
| ); | |
| } | |
| } else if ($request->request()->has('receiverMemNo') || ($request->request()->has('receiverNm') && $request->request()->has('receiverPhone'))) { | |
| $popupMode = true; | |
| // SMS 포인트 Sync | |
| Sms::saveSmsPoint(); | |
| /** | |
| * set view data | |
| */ | |
| $this->setData('reSenderData', $reSenderData); | |
| $this->setData('receiveTotal', $receiveTotal); | |
| $this->setData('rejectCount', $rejectCount); | |
| $this->setData('templateCategoryAutoCodes', $this->getTemplateCategoryAutoCodes()); | |
| $this->setData('replaceCodeGroupKey', $popupMode ? '' : 'member'); | |
| $this->setData('replaceCodeGroup', $replaceCodeGroup); | |
| $this->setData('replaceCodeTableGroups', SmsReplaceCode::GROUPS); | |
| // 엑셀 업로드 사용가능 여부 확인 | |
| $excelUploadUse = ComponentUtils::getPolicy('sms.config')['excelUploadUse']; | |
| return false; | |
| } | |
| } | |
| /** | |
| * 문구 그룹 코드는 상점이 코드 관리에서 추가·삭제·개명할 수 있어 DB 를 기준으로 삼는다 | |
| * | |
| * @return array 템플릿으로 저장 카테고리 선택지 (문구 그룹 코드 => 표기명) | |
| */ | |
| protected function getTemplateCategoryAutoCodes(): array | |
| { | |
| $autoCodes = []; | |
| foreach (SmsTemplateCategory::getNames() as $category => $name) { | |
| $autoCodes[SmsTemplateCategory::getAutoCode($category)] = $name; | |
| } | |
| $codeItems = gd_code(self::TEMPLATE_CATEGORY_CODE_GROUP); | |
| if (empty($codeItems)) { | |
| return $autoCodes; | |
| } | |
| $selections = []; | |
| foreach ($codeItems as $code => $codeName) { | |
| $category = SmsTemplateCategory::fromSmsAutoCode($code); | |
| $selections[$code] = $category !== '' ? SmsTemplateCategory::getName($category) : gd_htmlspecialchars($codeName); | |
| } | |
| return $selections; | |
| } | |
| } | |
변경 전SmsSendPsController.php
| namespace Bundle\Controller\Admin\Member; | |
| use Framework\Utility\StringUtils; | |
| /** | |
| * Class SmsSendPsController | |
| { | |
| public function index() | |
| { | |
| $request = \App::getInstance('request'); | |
| $logger = \App::getInstance('logger'); | |
| $logger->info(__METHOD__, $request->post()->all()); | |
| $this->json($responseParams); | |
| break; | |
| case 'getSmsContentsBox': // sms 발송내역 리스트 조회 | |
| /** | |
| * 리스트 조회 | |
| * | |
| $db->strWhere = 'smsType = \'user\' AND smsAutoCode = ?'; | |
| $db->bind_param_push($binds, $fieldTypes['smsAutoCode'], $request->post()->get('smsAutoCode')); | |
| } | |
| if ($request->post()->get('searchWord', '') != '') { | |
| $db->strWhere .= ' AND (subject LIKE CONCAT(\'%\', ?, \'%\') OR contents LIKE CONCAT(\'%\', ?, \'%\'))'; | |
| $db->bind_param_push($binds, $fieldTypes['subject'], $request->post()->get('searchWord')); | |
| $db->strWhere = 'smsType = \'user\' AND smsAutoCode = ?'; | |
| $db->bind_param_push($binds, $fieldTypes['smsAutoCode'], $request->post()->get('smsAutoCode')); | |
| } | |
| $query = $db->query_complete(); | |
| $strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . DB_SMS_CONTENTS . ' ' . implode(' ', $query); | |
| $data = $db->query_fetch($strSQL, $binds, false); | |
| $db->strWhere = 'smsType = \'user\' AND smsAutoCode = ?'; | |
| $db->bind_param_push($binds, $fieldTypes['smsAutoCode'], $request->post()->get('smsAutoCode')); | |
| } | |
| if ($request->post()->get('searchWord', '') != '') { | |
| $db->strWhere .= ' AND (subject LIKE CONCAT(\'%\', ?, \'%\') OR contents LIKE CONCAT(\'%\', ? , \'%\'))'; | |
| $db->bind_param_push($binds, $fieldTypes['subject'], $request->post()->get('searchWord')); | |
| $request = \App::getInstance('request'); | |
| $smsAdmin = \App::load('Component\\Sms\\SmsAdmin'); | |
| try { | |
| $isUpdate = $request->post()->has('sno'); | |
| $smsAdmin->validateSmsContents($request->post(), $isUpdate); | |
| if ($smsAdmin->saveSmsContentsData($request->post()->all())) { | |
| case 'deleteSmsContents': // 선택 발송내역 삭제 | |
| $request = \App::getInstance('request'); | |
| $smsAdmin = \App::load('Component\\Sms\\SmsAdmin'); | |
| if ($smsAdmin->deleteSmsContentsData($request->post()->get('sno', []))) { | |
| $this->json( | |
| [ | |
| 'result' => 'OK', | |
| 'message' => '삭제되었습니다.', | |
| ] | |
| ); | |
| } else { | |
| break; | |
| } | |
| } | |
| } | |
변경 후SmsSendPsController.php
| namespace Bundle\Controller\Admin\Member; | |
| use Framework\Utility\StringUtils; | |
| use Origin\Enum\Sms\Template\SmsTemplateApprovalStatus; | |
| use Origin\Service\Sms\Template\SmsTemplateApprovalService; | |
| use Origin\Service\Sms\Template\SmsTemplateService; | |
| /** | |
| * Class SmsSendPsController | |
| { | |
| public function index() | |
| { | |
| if (gd_is_provider()) { | |
| $this->setMenuCode('order', 'order', 'smsSend'); | |
| } else { | |
| $this->setMenuCode('member', 'sms', 'send'); | |
| } | |
| $request = \App::getInstance('request'); | |
| $logger = \App::getInstance('logger'); | |
| $logger->info(__METHOD__, $request->post()->all()); | |
| $this->json($responseParams); | |
| break; | |
| case 'getSmsContentsBox': // sms 발송내역 리스트 조회 | |
| $entryRestrictMessage = \App::getInstance(SmsTemplateApprovalService::class)->getEntryRestrictMessage(); | |
| if ($entryRestrictMessage !== null) { | |
| $this->json(['result' => 'FAIL', 'message' => $entryRestrictMessage,]); | |
| break; | |
| } | |
| /** | |
| * 리스트 조회 | |
| * | |
| $db->strWhere = 'smsType = \'user\' AND smsAutoCode = ?'; | |
| $db->bind_param_push($binds, $fieldTypes['smsAutoCode'], $request->post()->get('smsAutoCode')); | |
| } | |
| $db->strWhere .= ' AND approvalStatus = ?'; | |
| $db->bind_param_push($binds, 's', SmsTemplateApprovalStatus::APPROVED); | |
| if ($request->post()->get('searchWord', '') != '') { | |
| $db->strWhere .= ' AND (subject LIKE CONCAT(\'%\', ?, \'%\') OR contents LIKE CONCAT(\'%\', ?, \'%\'))'; | |
| $db->bind_param_push($binds, $fieldTypes['subject'], $request->post()->get('searchWord')); | |
| $db->strWhere = 'smsType = \'user\' AND smsAutoCode = ?'; | |
| $db->bind_param_push($binds, $fieldTypes['smsAutoCode'], $request->post()->get('smsAutoCode')); | |
| } | |
| $db->strWhere .= ' AND approvalStatus = ?'; | |
| $db->bind_param_push($binds, 's', SmsTemplateApprovalStatus::APPROVED); | |
| $query = $db->query_complete(); | |
| $strSQL = 'SELECT ' . array_shift($query) . ' FROM ' . DB_SMS_CONTENTS . ' ' . implode(' ', $query); | |
| $data = $db->query_fetch($strSQL, $binds, false); | |
| $db->strWhere = 'smsType = \'user\' AND smsAutoCode = ?'; | |
| $db->bind_param_push($binds, $fieldTypes['smsAutoCode'], $request->post()->get('smsAutoCode')); | |
| } | |
| $db->strWhere .= ' AND approvalStatus = ?'; | |
| $db->bind_param_push($binds, 's', SmsTemplateApprovalStatus::APPROVED); | |
| if ($request->post()->get('searchWord', '') != '') { | |
| $db->strWhere .= ' AND (subject LIKE CONCAT(\'%\', ?, \'%\') OR contents LIKE CONCAT(\'%\', ? , \'%\'))'; | |
| $db->bind_param_push($binds, $fieldTypes['subject'], $request->post()->get('searchWord')); | |
| $request = \App::getInstance('request'); | |
| $smsAdmin = \App::load('Component\\Sms\\SmsAdmin'); | |
| try { | |
| $blockMessage = $this->getSmsContentsWriteBlockMessage(); | |
| if ($blockMessage !== null) { | |
| $this->json(['result' => 'FAIL', 'message' => $blockMessage,]); | |
| break; | |
| } | |
| $isUpdate = $request->post()->has('sno'); | |
| $smsAdmin->validateSmsContents($request->post(), $isUpdate); | |
| if ($smsAdmin->saveSmsContentsData($request->post()->all())) { | |
| case 'deleteSmsContents': // 선택 발송내역 삭제 | |
| $request = \App::getInstance('request'); | |
| $smsAdmin = \App::load('Component\\Sms\\SmsAdmin'); | |
| $blockMessage = $this->getSmsContentsWriteBlockMessage(); | |
| if ($blockMessage !== null) { | |
| $this->json(['result' => 'FAIL', 'message' => $blockMessage,]); | |
| break; | |
| } | |
| $snoList = $request->post()->get('sno', []); | |
| $smsTemplateService = \App::getInstance(SmsTemplateService::class); | |
| $excluded = $smsTemplateService->excludeUsedByAutoSend(is_array($snoList) ? $snoList : []); | |
| $deletableSnoList = $smsTemplateService->filterLegacyDeletableSnoList($excluded['deletableSnoList']); | |
| if (empty($deletableSnoList) && $excluded['usedCount'] > 0) { | |
| $this->json(['result' => 'FAIL', 'message' => SmsTemplateService::USED_NOT_DELETABLE_MESSAGE,]); | |
| break; | |
| } | |
| if ($smsAdmin->deleteSmsContentsData($deletableSnoList)) { | |
| $this->json( | |
| [ | |
| 'result' => 'OK', | |
| 'message' => $excluded['usedCount'] > 0 | |
| ? sprintf(SmsTemplateService::PARTIAL_DELETED_MESSAGE, $excluded['usedCount'], count($deletableSnoList)) | |
| : '삭제되었습니다.', | |
| 'usedCount' => $excluded['usedCount'], | |
| ] | |
| ); | |
| } else { | |
| break; | |
| } | |
| } | |
| /** | |
| * @return string|null 차단 사유 문구, 통과 시 null | |
| */ | |
| protected function getSmsContentsWriteBlockMessage() | |
| { | |
| /** @var SmsTemplateApprovalService $approvalService */ | |
| $approvalService = \App::getInstance(SmsTemplateApprovalService::class); | |
| if (!$approvalService->hasPermission()) { | |
| return SmsTemplateService::NO_ROLE_MESSAGE; | |
| } | |
| if (!$approvalService->hasWritePermission()) { | |
| return SmsTemplateApprovalService::NO_WRITE_PERMISSION_MESSAGE; | |
| } | |
| return $approvalService->getWriteRestrictMessage(); | |
| } | |
| } | |
변경 전ManagePsController.php
| $this->setData('sno', $request->get()->get('sno')); | |
| // 최고운영자/위임자면 발송담당자-SMS(messageSendSms) 체크 고정+잠금 (뷰에서 사용) | |
| $this->setData('isMessageApprover', $messageOperatorRoleService->isSenderPermissionLocked((int)$request->get()->get('sno'))); | |
| $this->setData('isEditorMessageApprover', $messageOperatorRoleService->isApprover((int)$session->get('manager.sno'))); | |
| /** @var MessageOperatorRoleGuard $messageOperatorRoleGuard */ | |
| $messageOperatorRoleGuard = \App::getInstance(MessageOperatorRoleGuard::class); | |
| $isEditorMessageRepresentative = $messageOperatorRoleGuard->isRepresentative(); | |
| $this->setData('isEditorMessageRepresentative', $isEditorMessageRepresentative); | |
| // 차단 조회는 외부 API 호출이라 책임자 화면에서만 수행한다 | |
| $this->setData('isCallerBlocked', $isEditorMessageRepresentative && $messageOperatorRoleService->isBlockedRepresentative()); | |
| $this->setData('messageSenderCount', $messageOperatorRoleService->countSenders()); | |
| $this->setData('messageSenderLimit', MessageOperatorRoleService::MAX_SENDER_COUNT); | |
| break; | |
변경 후ManagePsController.php
| $this->setData('sno', $request->get()->get('sno')); | |
| // 최고운영자/위임자면 발송담당자-SMS(messageSendSms) 체크 고정+잠금 (뷰에서 사용) | |
| $this->setData('isMessageApprover', $messageOperatorRoleService->isSenderPermissionLocked((int)$request->get()->get('sno'))); | |
| /** @var MessageOperatorRoleGuard $messageOperatorRoleGuard */ | |
| $messageOperatorRoleGuard = \App::getInstance(MessageOperatorRoleGuard::class); | |
| $isEditorMessageRepresentative = $messageOperatorRoleGuard->isRepresentative(); | |
| $this->setData('isEditorMessageRepresentative', $isEditorMessageRepresentative); | |
| $this->setData('isEditorMessageApprover', $messageOperatorRoleGuard->isDelegateOrHigher()); | |
| // 차단 조회는 외부 API 호출이라 책임자 화면에서만 수행한다 | |
| $this->setData('isCallerBlocked', $isEditorMessageRepresentative && $messageOperatorRoleService->isBlockedRepresentative()); | |
| // 위임자 아님 안내가 우선이라 위임자 이상일 때만 판정한다 — isUncertifiedDelegateActor 는 위임자가 아닌 운영자도 DI 가 없으면 true 다 | |
| $this->setData('isCallerUncertifiedDelegate', $messageOperatorRoleGuard->isDelegateOrHigher() && $messageOperatorRoleService->isUncertifiedDelegateActor()); | |
| $this->setData('messageSenderCount', $messageOperatorRoleService->countSenders()); | |
| $this->setData('messageSenderLimit', MessageOperatorRoleService::MAX_SENDER_COUNT); | |
| break; | |
변경 전ManageRegisterController.php
| $this->setData('messageDelegate', $messageDelegate); | |
| // 발송 역할 계정(책임자·위임자·발송담당자)의 휴대폰번호는 본인인증(DI 주입)으로 처리한다 | |
| // 본인인증은 본인 계정에서만 성립하므로 책임자·제3자가 여는 화면은 기존 점유인증(SMS) UI 를 그대로 쓴다 | |
| $isSelfTarget = $isModify && (int)$session->get('manager.sno') === $managerNo; | |
| // 최고운영자·발송 역할 계정은 정보 수정 주체를 책임자와 본인으로 제한한다 | |
| $isRestrictedEditTarget = $isModify | |
| $isSelfCertificationProvided = $managerNo === ManagerService::SUPER_MANAGER_SNO | |
| ? $managerEditPolicy->isRepresentativeSelfCertificationProvided($managerNo) | |
| : $isRestrictedEditTarget; | |
| if ($isSelfTarget && $isSelfCertificationProvided) { | |
| $selfCertification = [ | |
| 'enabled' => true, | |
| $this->addScript([ | |
| 'manager-certification.js?' . time(), | |
| ]); | |
| } | |
| $this->setData('selfCertification', $selfCertification); | |
| // 인증 방식 분기와 분리해 둔다 — 본인인증 화면이 본인 한정이 되어도 책임자·제3자 화면의 1차 차단은 남아야 한다 | |
| // 이름·휴대폰번호는 본인인증 명의라 직접 수정할 수 없다 — 발송 역할 계정 본인 화면과 책임자 본인 화면에서 잠근다 | |
| // 이름은 발송 역할 계정이면 책임자에게도 잠기고, 휴대폰번호는 책임자에게만 열린다 | |
| $this->setData('senderNameLocked', ($isModify && $managerEditPolicy->isSenderNameLocked($managerNo)) || $isRepresentativeSelf); | |
| $this->setData('senderCellPhoneLocked', ($isModify && $managerEditPolicy->isSenderCellPhoneLocked($managerNo)) || $isRepresentativeSelf); | |
| // --- 관리자 디자인 템플릿 | |
| $this->setData('isPasswordChangeRestricted', $isCompanySuperManager && $isMallInstalledAfterPatch); | |
변경 후ManageRegisterController.php
| $this->setData('messageDelegate', $messageDelegate); | |
| // 발송 역할 계정(책임자·위임자·발송담당자)의 휴대폰번호는 본인인증(DI 주입)으로 처리한다 | |
| // 본인인증은 본인 계정에서만 성립하므로 책임자가 여는 화면은 기존 점유인증(SMS) UI 를 그대로 쓴다 | |
| $isSelfTarget = $isModify && (int)$session->get('manager.sno') === $managerNo; | |
| // 최고운영자·발송 역할 계정은 정보 수정 주체를 책임자와 본인으로 제한한다 | |
| $isRestrictedEditTarget = $isModify | |
| $isSelfCertificationProvided = $managerNo === ManagerService::SUPER_MANAGER_SNO | |
| ? $managerEditPolicy->isRepresentativeSelfCertificationProvided($managerNo) | |
| : $isRestrictedEditTarget; | |
| // 책임자도 본인도 아닌 운영자에게도 발송 역할 계정의 인증 방식은 본인인증으로 노출한다 — 인증 스크립트는 싣지 않고 클릭은 본인 아님 안내로 끝난다 | |
| $isOtherEditorOfSenderRole = $isModify | |
| && $managerNo !== ManagerService::SUPER_MANAGER_SNO | |
| && $isRestrictedEditTarget | |
| && !$isSelfTarget | |
| && !$messageOperatorRoleGuard->isRepresentative(); | |
| if ($isSelfTarget && $isSelfCertificationProvided) { | |
| $selfCertification = [ | |
| 'enabled' => true, | |
| $this->addScript([ | |
| 'manager-certification.js?' . time(), | |
| ]); | |
| } elseif ($isOtherEditorOfSenderRole) { | |
| $selfCertification = [ | |
| 'enabled' => true, | |
| 'certified' => $messageOperatorRoleGuard->isConnected($managerNo), | |
| 'isSelf' => false, | |
| ]; | |
| } | |
| $this->setData('selfCertification', $selfCertification); | |
| // 인증 방식 분기와 분리해 둔다 — 본인인증 화면이 본인 한정이 되어도 책임자·제3자 화면의 1차 차단은 남아야 한다 | |
| // 이름·휴대폰번호는 본인인증 명의라 직접 수정할 수 없다 — 발송 역할 계정 본인 화면과 책임자 본인 화면에서 잠근다 | |
| // 이름은 발송 역할 계정이면 책임자에게도 잠기고, 휴대폰번호는 책임자에게만 열린다 | |
| // 그 외 운영자 화면의 휴대폰 입력은 기획상 활성으로 노출한다 — 저장은 화면 1차 차단과 저장측 되돌림이 막는다 | |
| $this->setData('senderNameLocked', ($isModify && $managerEditPolicy->isSenderNameLocked($managerNo)) || $isRepresentativeSelf); | |
| $this->setData( | |
| 'senderCellPhoneLocked', | |
| !$isOtherEditorOfSenderRole | |
| && (($isModify && $managerEditPolicy->isSenderCellPhoneLocked($managerNo)) || $isRepresentativeSelf) | |
| ); | |
| // --- 관리자 디자인 템플릿 | |
| $this->setData('isPasswordChangeRestricted', $isCompanySuperManager && $isMallInstalledAfterPatch); | |
변경 전CartRemindListController.php
| { | |
| // --- 메뉴 설정 | |
| $this->callMenu('promotion', 'cartRemind', 'cartRemindList'); | |
| /** @var SmsManageCallNumberService $smsManageCallNumberService */ | |
| $smsManageCallNumberService = \App::getInstance(SmsManageCallNumberService::class); | |
변경 후CartRemindListController.php
| { | |
| // --- 메뉴 설정 | |
| $this->callMenu('promotion', 'cartRemind', 'cartRemindList'); | |
| $this->setMenuCode('promotion', 'cartRemind', 'cartRemindList'); | |
| /** @var SmsManageCallNumberService $smsManageCallNumberService */ | |
| $smsManageCallNumberService = \App::getInstance(SmsManageCallNumberService::class); | |
변경 전CartRemindPsController.php
| <?php | |
| /** | |
| * This is commercial software, only users who have purchased a valid license | |
| * and accept to the terms of the License Agreement can install and use this | |
| * program. | |
| * | |
| * Do not edit or add to this file if you wish to upgrade Godomall5 to newer | |
| * versions in the future. | |
| * | |
| * @copyright ⓒ 2016, NHN godo: Corp. | |
| * @link http://www.godo.co.kr | |
| */ | |
| namespace Bundle\Controller\Admin\Promotion; | |
| use Component\CartRemind\CartRemind; | |
| use Component\Sms\Exception\PasswordException; | |
| use Framework\Debug\Exception; | |
| use Framework\Debug\Exception\LayerNotReloadException; | |
| use Message; | |
| use Request; | |
| class CartRemindPsController extends \Controller\Admin\Controller | |
| { | |
| /** | |
| * 장바구니 알림 처리 | |
| * [관리자 모드] 장바구니 알림 처리 | |
| * | |
| * @author su | |
| */ | |
| public function index() | |
| { | |
| try { | |
| $cartRemind = new CartRemind(); | |
| switch (Request::post()->get('mode')) { | |
| case 'insertCartRemind': | |
| case 'modifyCartRemind': | |
| // 모듈 호출 | |
| $postValue = Request::post()->toArray(); | |
| $cartRemind->setCartRemind($postValue); | |
| $this->layer(__('저장이 완료되었습니다.'), 'parent.location.href="cart_remind_list.php";'); | |
| break; | |
| case 'modifyCartRemindMessage': | |
| $postValue = Request::post()->toArray(); | |
| $cartRemind->setCartRemindSendMessage($postValue); | |
| $this->layer(__('수정되었습니다.'), 'parent.location.href="cart_remind_list.php";'); | |
| break; | |
| case 'sendCartRemindManual': | |
| $postValue = Request::post()->toArray(); | |
| try { | |
| $result = $cartRemind->sendCartRemind($postValue); | |
| } catch (PasswordException $e) { | |
| $result['msg'] = $e->getMessage(); | |
| } | |
| echo json_encode($result); | |
| exit; | |
| break; | |
| case 'setCartRemindAutoState': | |
| $postValue = Request::post()->toArray(); | |
| $cartRemind->setCartRemindAutoState($postValue); | |
| $result['code'] = 99; | |
| $result['msg'] = __('변경되었습니다.'); | |
| echo json_encode($result); | |
| exit; | |
| break; | |
| case 'deleteCartRemind': | |
| $postValue = Request::post()->toArray(); | |
| $cartRemind->deleteCartRemind($postValue); | |
| $this->layer(__('삭제되었습니다.'), 'parent.location.href="cart_remind_list.php";'); | |
| break; | |
| } | |
| } catch (\Exception $e) { | |
| throw new LayerNotReloadException($e->getMessage()); //새로고침안됨 | |
| } | |
| } | |
| } | |
변경 후CartRemindPsController.php
변경 전ComebackCouponListController.php
| { | |
| // --- 메뉴 설정 | |
| $this->callMenu('promotion', 'coupon', 'comebackCouponList'); | |
| // --- 모듈 호출 | |
| try { | |
변경 후ComebackCouponListController.php
| { | |
| // --- 메뉴 설정 | |
| $this->callMenu('promotion', 'coupon', 'comebackCouponList'); | |
| $this->setMenuCode('promotion', 'coupon', 'comebackCouponList'); | |
| // --- 모듈 호출 | |
| try { | |
변경 전CouponPsController.php
| use Component\Sms\SmsSender; | |
| use Framework\Debug\Exception\LayerNotReloadException; | |
| use Message; | |
| use Request; | |
| class CouponPsController extends \Controller\Admin\Controller | |
| $this->json(array('count' => $aComebackCouponMemberList, 'checkCoupon' => $checkCoupon)); | |
| break; | |
| case 'sendComebackCoupon': | |
| $aSno = Request::post()->get('chkCoupon'); | |
| $couponAdmin->sendComebackCoupon($aSno[0]); | |
| $this->layer(__('컴백쿠폰 발송이 완료되었습니다.'), 'top.location.href="comeback_coupon_list.php";'); | |
| break; | |
변경 후CouponPsController.php
| use Component\Sms\SmsSender; | |
| use Framework\Debug\Exception\LayerNotReloadException; | |
| use Message; | |
| use Origin\Service\Sms\OccupancyAuth\SendGateService; | |
| use Request; | |
| class CouponPsController extends \Controller\Admin\Controller | |
| $this->json(array('count' => $aComebackCouponMemberList, 'checkCoupon' => $checkCoupon)); | |
| break; | |
| case 'sendComebackCoupon': | |
| $this->setMenuCode('promotion', 'coupon', 'comebackCouponList'); | |
| $aSno = Request::post()->get('chkCoupon'); | |
| $comebackCouponInfo = $couponAdmin->getComebackCouponInfo($aSno[0], 'smsFl'); | |
| if (($comebackCouponInfo['smsFl'] ?? '') === 'y') { | |
| /** @var SendGateService $sendGateService */ | |
| $sendGateService = \App::getInstance(SendGateService::class); | |
| $sendGateService->assertSendable(); | |
| $sendGateService->assertCallNumberUsable(); | |
| } | |
| $couponAdmin->sendComebackCoupon($aSno[0]); | |
| $this->layer(__('컴백쿠폰 발송이 완료되었습니다.'), 'top.location.href="comeback_coupon_list.php";'); | |
| break; | |
변경 전LayerManagePermissionController.php
| $this->setData('disabled', $disabled); | |
| $this->setData('sno', $request->post()->get('sno')); | |
| $this->setData('isMessageApprover', $isMessageApprover); | |
| $this->setData('isEditorMessageApprover', $messageOperatorRoleService->isApprover((int)$session->get('manager.sno'))); | |
| $this->setData('isEditorMessageRepresentative', $isEditorMessageRepresentative); | |
| // 차단 조회는 외부 API 호출이라 책임자 화면에서만 수행한다 | |
| $this->setData('isCallerBlocked', $isEditorMessageRepresentative && $messageOperatorRoleService->isBlockedRepresentative()); | |
| $this->setData('messageSenderCount', $messageOperatorRoleService->countSenders()); | |
| $this->setData('messageSenderLimit', MessageOperatorRoleService::MAX_SENDER_COUNT); | |
| $this->setData('scmFl', $scmFl); | |
변경 후LayerManagePermissionController.php
| $this->setData('disabled', $disabled); | |
| $this->setData('sno', $request->post()->get('sno')); | |
| $this->setData('isMessageApprover', $isMessageApprover); | |
| $this->setData('isEditorMessageApprover', $messageOperatorRoleGuard->isDelegateOrHigher()); | |
| $this->setData('isEditorMessageRepresentative', $isEditorMessageRepresentative); | |
| // 차단 조회는 외부 API 호출이라 책임자 화면에서만 수행한다 | |
| $this->setData('isCallerBlocked', $isEditorMessageRepresentative && $messageOperatorRoleService->isBlockedRepresentative()); | |
| // 위임자 아님 안내가 우선이라 위임자 이상일 때만 판정한다 — isUncertifiedDelegateActor 는 위임자가 아닌 운영자도 DI 가 없으면 true 다 | |
| $this->setData('isCallerUncertifiedDelegate', $messageOperatorRoleGuard->isDelegateOrHigher() && $messageOperatorRoleService->isUncertifiedDelegateActor()); | |
| $this->setData('messageSenderCount', $messageOperatorRoleService->countSenders()); | |
| $this->setData('messageSenderLimit', MessageOperatorRoleService::MAX_SENDER_COUNT); | |
| $this->setData('scmFl', $scmFl); | |
변경 전LayerSmsContentsController.php
| <?php | |
| /** | |
| * This is commercial software, only users who have purchased a valid license | |
| * and accept to the terms of the License Agreement can install and use this | |
| * program. | |
| * | |
| * Do not edit or add to this file if you wish to upgrade Godomall5 to newer | |
| * versions in the future. | |
| * | |
| * @copyright ⓒ 2016, NHN godo: Corp. | |
| * @link http://www.godo.co.kr | |
| */ | |
| namespace Bundle\Controller\Admin\Share; | |
| use Component\Sms\SmsAdmin; | |
| use Request; | |
| /** | |
| * SMS 문구 선택하기 | |
| * | |
| * @author Shin Donggyu <artherot@godo.co.kr> | |
| */ | |
| class LayerSmsContentsController extends \Controller\Admin\Controller | |
| { | |
| /** | |
| * index | |
| * | |
| */ | |
| public function index() | |
| { | |
| // --- SMS 모듈 | |
| $smsAdmin = new SmsAdmin(); | |
| // mode 가 있는 경우 | |
| if (Request::post()->has('mode')) { | |
| // _POST 데이터 | |
| $postValue = Request::post()->toArray(); | |
| switch (Request::post()->get('mode')) { | |
| // 등록 / 수정 | |
| case 'registerSmsContents': | |
| case 'modifySmsContents': | |
| $result = $smsAdmin->saveSmsContentsData($postValue); | |
| if ($result === true) { | |
| echo 'OK'; | |
| } else { | |
| echo 'ERROR'; | |
| } | |
| break; | |
| // 선택 삭제 | |
| case 'deleteSmsContents': | |
| $result = $smsAdmin->deleteSmsContentsData(Request::post()->get('delSno')); | |
| if ($result === true) { | |
| echo 'OK'; | |
| } else { | |
| echo 'ERROR'; | |
| } | |
| break; | |
| } | |
| exit(); | |
| } | |
| // SMS 문구 그룹 | |
| $smsContentsGroup = gd_code('01007'); | |
| $smsContentsGroup = array_merge(['0' => '문구 선택'], $smsContentsGroup); | |
| // SMS 문구 리스트 | |
| $smsContentsList = $smsAdmin->getSmsContentsList('user'); | |
| // _GET Data | |
| $getValue = Request::get()->toArray(); | |
| // page Url | |
| $pageUrl = '../' . Request::getDirectoryUri() . '/' . Request::getFileUri(); | |
| //--- 관리자 디자인 템플릿 | |
| $this->getView()->setDefine('layout', 'layout_layer.php'); | |
| $this->getView()->setDefine('layoutContent', Request::getDirectoryUri() . '/' . Request::getFileUri()); | |
| $this->setData('layerFormID', $getValue['layerFormID']); | |
| $this->setData('parentFormID', $getValue['parentFormID']); | |
| $this->setData('dataFormID', $getValue['dataFormID']); | |
| $this->setData('dataInputNm', $getValue['dataInputNm']); | |
| $this->setData('mode', gd_isset($getValue['mode'], 'search')); | |
| $this->setData('disabled', gd_isset($getValue['disabled'], '')); | |
| $this->setData('callFunc', gd_isset($getValue['callFunc'], '')); | |
| $this->setData('pageUrl', $pageUrl); | |
| $this->setData('smsContentsGroup', $smsContentsGroup); | |
| $this->setData($smsContentsList); | |
| } | |
| } | |
변경 후LayerSmsContentsController.php
변경 전LayerSmsPasswordPsController.php
| use Component\Sms\Exception\PasswordException; | |
| use Component\Sms\SmsSender; | |
| use Framework\Utility\ComponentUtils; | |
| use Vendor\Captcha\Captcha; | |
| class LayerSmsPasswordPsController extends \Controller\Admin\Controller | |
| $request = \App::getInstance('request'); | |
| $isChangePassword = $request->post()->get('mode', 'changePassword') === 'changePassword'; | |
| if ($isChangePassword) { | |
| $captchaNumber = $request->post()->get('captcha'); | |
| $captcha = new Captcha(); | |
| $result = $captcha->verify($captchaNumber, 1); | |
| ); | |
| } | |
| } | |
| } | |
변경 후LayerSmsPasswordPsController.php
| use Component\Sms\Exception\PasswordException; | |
| use Component\Sms\SmsSender; | |
| use Framework\Utility\ComponentUtils; | |
| use Origin\Enum\Crm\Message\MessageSendMenuAccessVerdict; | |
| use Origin\Service\Crm\Message\MessageSendMenuAccessService; | |
| use Origin\Service\Sms\Template\SmsTemplateService; | |
| use Vendor\Captcha\Captcha; | |
| class LayerSmsPasswordPsController extends \Controller\Admin\Controller | |
| $request = \App::getInstance('request'); | |
| $isChangePassword = $request->post()->get('mode', 'changePassword') === 'changePassword'; | |
| if ($isChangePassword) { | |
| $this->setMenuCode('member', 'sms', 'auto'); | |
| $restrictMessage = $this->getSmsRestrictMessage(); | |
| if ($restrictMessage !== null) { | |
| $this->json(['error' => 1, 'message' => $restrictMessage]); | |
| } | |
| $captchaNumber = $request->post()->get('captcha'); | |
| $captcha = new Captcha(); | |
| $result = $captcha->verify($captchaNumber, 1); | |
| ); | |
| } | |
| } | |
| /** | |
| * SMS 인증번호 변경은 SMS 관리자만 가능하다. | |
| * ps 는 인터셉터 게이트를 타지 않아 화면 제한을 우회한 직접 호출이 가능하므로 조작 시점에 다시 본다. | |
| * 판정축은 진입 게이트와 동일(역할·차단·IP·진입 점유인증). 책임자 미연동은 등록 안내 플로우라 차단하지 않는다. | |
| */ | |
| protected function getSmsRestrictMessage() | |
| { | |
| /** @var MessageSendMenuAccessService $menuAccessService */ | |
| $menuAccessService = \App::getInstance(MessageSendMenuAccessService::class); | |
| $verdict = $menuAccessService->evaluateEntry((int)\App::getInstance('session')->get('manager.sno')); | |
| $writableVerdicts = [ | |
| null, | |
| MessageSendMenuAccessVerdict::NOT_CONNECTED_REPRESENTATIVE, | |
| MessageSendMenuAccessVerdict::NOT_CONNECTED_MANAGER, | |
| ]; | |
| if (in_array($verdict, $writableVerdicts, true)) { | |
| return null; | |
| } | |
| $message = MessageSendMenuAccessVerdict::getAlertMessage($verdict); | |
| if ($message === '') { | |
| $message = $verdict === MessageSendMenuAccessVerdict::NO_ROLE | |
| ? SmsTemplateService::NO_ROLE_MESSAGE | |
| : MessageSendMenuAccessVerdict::getAlertMessage(MessageSendMenuAccessVerdict::POSSESSION_AUTH_REQUIRED); | |
| } | |
| return $message; | |
| } | |
| } | |