본문으로 건너뛰기

소스 변경 전체 보기

PHP 7 · GODO25
변경 파일 수: 36개
변경 파일 목록
총 36개
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/
변경 파일 코드
추가삭제
SMS 발송 보안 강화
Admin/member/layer_sms_template_approval_history.php+36
변경 전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>
 
Admin/member/layer_sms_template_auto_send.php+33
변경 전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>
 
Admin/member/layer_sms_template_form.php+218
변경 전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>
 
Admin/member/layer_sms_template_load.php+128
변경 전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>
 
Admin/member/layer_sms_template_preview.php+20
변경 전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>
 
Admin/member/sms_auto.php+62-40
변경 전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 () {
 
Admin/member/_sms_replace_code_table.php+31
변경 전_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>
 
Admin/member/sms_send.php+82-140
변경 전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>
 
Admin/member/sms_template.php+351-1
변경 전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>
 
Admin/policy/_manage_permission_menu_list.php+1-1
변경 전_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">
 
Admin/policy/manage_permission.php+4
변경 전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;
 
Admin/policy/manage_register.php+8-4
변경 전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)) {
 
Admin/policy/manage_security.php+1-2
변경 전manage_security.php
화면보안접속 사용 시 보안접속 인증화면이 노출되는 화면<br>
- 기본설정>관리정책>운영자관리/운영자등록<br>
- 기본설정>관리정책>운영보안설정<br>
- 기본설정>결제정책>무통장입금은행관리<br>
- 회원>SMS관리>개별/전체 SMS 발송
</p>
</div>
</td>
 
변경 후manage_security.php
화면보안접속 사용 시 보안접속 인증화면이 노출되는 화면<br>
- 기본설정>관리정책>운영자관리/운영자등록<br>
- 기본설정>관리정책>운영보안설정<br>
- 기본설정>결제정책>무통장입금은행관리
 
</p>
</div>
</td>
 
Admin/promotion/cart_remind_list.php+10
변경 전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,
 
Admin/promotion/comeback_coupon_list.php+13
변경 전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,
 
Admin/share/layer_message_representative_register_guide.php-136
변경 전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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Admin/share/layer_message_representative_send_restrict.php+128
변경 전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>
 
Admin/share/layer_occupancy_auth.php+400
변경 전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; ?>
 
Admin/share/layer_sms_contents.php+14-5
변경 전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">
등록/수정한 문구는 승인 후 발송 가능합니다. 아래 목록에는 승인된 문구만 노출되며, 승인 처리는 회원 &gt; SMS 관리 &gt; 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 : '삭제시 오류가 발생하였습니다.', '정보');
}
});
}
 
Component/Admin/AdminMenu.php+40-1
변경 전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;
 
Component/Database/DBTableField.php+5
변경 전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)
 
Component/Member/Manager.php+17-4
변경 전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';
 
Component/Sms/SmsAdmin.php+85-62
변경 전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));
}
 
Component/Sms/SmsAuto.php+44-4
변경 전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);
}
 
/**
 
Controller/Admin/Member/SmsAutoController.php+6
변경 전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);
 
Controller/Admin/Member/SmsSendController.php+40-18
변경 전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;
}
}
 
Controller/Admin/Member/SmsSendPsController.php+60-3
변경 전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();
}
}
 
Controller/Admin/Policy/ManagePsController.php+3-1
변경 전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;
 
Controller/Admin/Policy/ManageRegisterController.php+19-2
변경 전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);
 
Controller/Admin/Promotion/CartRemindListController.php+1
변경 전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);
 
Controller/Admin/Promotion/CartRemindPsController.php-77
변경 전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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Controller/Admin/Promotion/ComebackCouponListController.php+1
변경 전ComebackCouponListController.php
{
// --- 메뉴 설정
$this->callMenu('promotion', 'coupon', 'comebackCouponList');
 
 
// --- 모듈 호출
try {
 
변경 후ComebackCouponListController.php
{
// --- 메뉴 설정
$this->callMenu('promotion', 'coupon', 'comebackCouponList');
$this->setMenuCode('promotion', 'coupon', 'comebackCouponList');
 
// --- 모듈 호출
try {
 
Controller/Admin/Promotion/CouponPsController.php+9
변경 전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;
 
Controller/Admin/Share/LayerManagePermissionController.php+3-1
변경 전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);
 
Controller/Admin/Share/LayerSmsContentsController.php-95
변경 전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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Controller/Admin/Share/LayerSmsPasswordPsController.php+41
변경 전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;
}
}