Repository navigation
Expand file tree
/
Copy pathduckhoo-redesign.php
More file actions
302 lines (259 loc) · 17.8 KB
/
Copy pathduckhoo-redesign.php
File metadata and controls
302 lines (259 loc) · 17.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
<?php
/**
* Plugin Name: 액상덕후 리디자인
* Plugin URI: https://github.com/officialvafriend/new-website-build
* Description: 액상덕후 리디자인 — 홈 화면(실제 상품 연동), 입금 전 주문취소, 회원탈퇴. 테마와 keyple 플러그인은 건드리지 않습니다.
* Version: 0.5.0
* Requires at least: 6.5
* Requires PHP: 8.1
* Author: officialvafriend
* License: GPL-2.0-or-later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: duckhoo-redesign
*
* @package DuckhooRedesign
*/
declare( strict_types = 1 );
namespace Duckhoo\Redesign;
// 워드프레스를 거치지 않은 직접 접근을 막습니다.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
const VERSION = '0.5.0';
// 회원탈퇴 — 원래 페이지가 비어 있어서 여기서 채웁니다.
require_once plugin_dir_path( __FILE__ ) . 'includes/membership-cancel.php';
// 홈 — 첫 화면을 플러그인이 그립니다. 테마 파일은 건드리지 않습니다.
require_once plugin_dir_path( __FILE__ ) . 'includes/front.php';
// 껍데기 — 홈 말고 모든 화면에 우리 헤더·푸터·탭바를 씌웁니다. 테마 템플릿은 그대로 돕니다.
require_once plugin_dir_path( __FILE__ ) . 'includes/shell.php';
// 안내 페이지(이용약관 · 개인정보처리방침 · 배송/교환/환불) — 없으면 만든다.
require_once plugin_dir_path( __FILE__ ) . 'includes/pages.php';
// 상품 상세의 구매 상자.
require_once plugin_dir_path( __FILE__ ) . 'includes/product.php';
// 계정 · 회원가입 화면 — 머리판과 가입 단계.
require_once plugin_dir_path( __FILE__ ) . 'includes/account.php';
// 적립금을 쓴 주문의 취소 — 되돌아오지 않는 적립금을 손님이 혼자 잃지 않게.
require_once plugin_dir_path( __FILE__ ) . 'includes/points.php';
// 사진 후기 — 사진을 올린 후기에 적립금.
require_once plugin_dir_path( __FILE__ ) . 'includes/review-photos.php';
// 9월 금액대별 자동 할인을 끈다 (사장님 결정 2026-09-09). 테마 파일은 건드리지 않는다.
require_once plugin_dir_path( __FILE__ ) . 'includes/discount.php';
// 계정 정책 — 같은 휴대폰번호로 두 번 가입하지 못하게.
require_once plugin_dir_path( __FILE__ ) . 'includes/signup.php';
// 노보 물량 이벤트 — 하루 구매 한도 · 남은 수량 · 이벤트 기준 가격.
require_once plugin_dir_path( __FILE__ ) . 'includes/novo.php';
// 구매 깔때기 — 첫 화면부터 주문까지 날마다 몇 명이 어디까지 갔는지 센다 (매출 화면에 그린다).
require_once plugin_dir_path( __FILE__ ) . 'includes/funnel.php';
// 가입 · 로그인 뒤 보던 상품으로 돌아온다 — 키플 가입 흐름이 redirect_to 를 버리기 때문.
require_once plugin_dir_path( __FILE__ ) . 'includes/back.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/agree-gate.php'; // 가입 2단계(약관) 건너뛰기 막기 · 수신 동의 기록
// 검색 노출 — 네이버 인증 메타 · 상품 한 줄 설명(글) · 분류 메타 설명 · 브랜드 페이지 /brand/<slug>/.
require_once plugin_dir_path( __FILE__ ) . 'includes/seo.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/seo-texts.php'; // 사장님이 승인한 상품 글 (상자가 비었을 때만)
require_once plugin_dir_path( __FILE__ ) . 'includes/seo-pages.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/novo-seo.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/brand-seo.php'; // 브랜드 페이지 「종류 · 가격」 표 · FAQ · 날짜 제목 (2026-10-20)
require_once plugin_dir_path( __FILE__ ) . 'includes/broad-seo.php'; // 「전담 액상」 목표 — 홈 · 입호흡 · 폐호흡 · 전체 상품의 제목 · 설명 · FAQ (2026-10-01) // 노보 품절 국면 — 다른 맛 링크 · FAQ · 안내 글 초안 (2026-10-01)
require_once plugin_dir_path( __FILE__ ) . 'includes/help.php'; // 도움말 화면 — 배송 · 고객센터 · 자주 묻는 질문 · 공지 · 1:1 문의 머리판과 본문 (2026-10-09)
require_once plugin_dir_path( __FILE__ ) . 'includes/pricelist.php'; // /price/ 가격표 숏코드 (값을 묻는 검색에 답한다) // 네이버 진단 — 페이지 설명 · noindex · 사이트맵 · 이미지 alt
require_once plugin_dir_path( __FILE__ ) . 'includes/mail.php'; // 메일 발신자 이름 · 비밀번호 찾기 (워드프레스 기본 화면으로 새지 않게)
require_once plugin_dir_path( __FILE__ ) . 'includes/referral.php'; // 친구 추천 — 개인 코드 · 링크 · 첫 주문 입금확인 뒤 두 사람에게 적립금 (마케팅 → 친구 추천에서 켠다)
require_once plugin_dir_path( __FILE__ ) . 'includes/welcome-coupon.php'; // 가입 쿠폰 — 새 회원에게 그 사람만 쓰는 쿠폰 한 장 (마케팅 → 가입 쿠폰에서 켠다)
require_once plugin_dir_path( __FILE__ ) . 'includes/sms.php'; // 자동 문자 — 주문 입금 안내 (마케팅 → 자동 문자 · 끔/시험/켬) · 장바구니 담은 시각
// 목록 정렬 — 가격 낮은 순 · 높은 순. 검색 결과는 워드커머스가 정렬하지 않아 우리가 한다.
require_once plugin_dir_path( __FILE__ ) . 'includes/sort.php';
// 송장번호를 손님 화면에 보여 준다 — 관리자에만 있고 주문 상세에는 없었다. 읽기만 한다.
require_once plugin_dir_path( __FILE__ ) . 'includes/tracking.php';
// 후기를 부른다 — 쓸 길이 상품 상세 한 곳뿐이라 받은 손님이 찾아갈 수가 없었다. 읽기만 한다.
require_once plugin_dir_path( __FILE__ ) . 'includes/review-ask.php';
// 후기 화면 — 이름 가리기(김**) · 아바타 빼기 · 날짜 꼴.
require_once plugin_dir_path( __FILE__ ) . 'includes/review-ui.php';
// 담아 둔 뒤 값이 바뀌면 옛 금액으로 주문된다 — 결제에서 막는다.
require_once plugin_dir_path( __FILE__ ) . 'includes/price-check.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/must-pick.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/inquiry-who.php'; // 1:1 문의 게시판에서 직원에게만 회원 번호 · 연락처 · 주문 수 (동명이인)
require_once plugin_dir_path( __FILE__ ) . 'includes/stock-cap.php'; // 재고 수량보다 많이 담기 막기 — 테마 빌더의 「기본 상품」 수량까지 센다 (2026-10-14, 그린펀치 1병)
require_once plugin_dir_path( __FILE__ ) . 'includes/blocked.php'; // 팔지 않는 맛 — 낱병 · 묶음 맛 옵션 · 담아 둔 장바구니 어디서든 못 사게 (2026-10-19, 그린펀치)
require_once plugin_dir_path( __FILE__ ) . 'includes/bulk-sets.php'; // 한 상품 안에서 병 수 고르기 — 세트 수 + 할인 줄 (대상은 config() 의 상품 번호, 비면 아무 일도 안 한다)
require_once plugin_dir_path( __FILE__ ) . 'includes/twin-login.php'; // 같은 아이디 계정이 둘일 때 로그인을 맞는 쪽으로 (비밀번호 검증은 그대로) // 옵션(색상)을 고르지 않으면 못 담는다 — 서버 쪽 빗장
// 옛 회원 재인증 문 — 인증 기록도 옛 사이트 확인 표시도 없는 회원만 결제 전에 테마 재인증 화면으로 (2026-09-24)
require_once plugin_dir_path( __FILE__ ) . 'includes/verify-gate.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/today.php'; // 오늘 할 일 — 화면 · 대시보드 위젯 · 아침 메일(크론이라 admin 밖)
require_once plugin_dir_path( __FILE__ ) . 'includes/brief.php'; // 클로드 아침 브리핑 — 집계 숫자를 키로 닫힌 REST 주소로 (REST 라 admin 밖)
require_once plugin_dir_path( __FILE__ ) . 'includes/seo-report.php'; // SEO 월간 보고서 — 작업 일지 REST · 유입 · 상품별 조회 (REST · 크론이라 admin 밖)
require_once plugin_dir_path( __FILE__ ) . 'includes/imweb.php'; // 아임웹(옛 사이트) 주문 합치기 — 월말 결산이 부른다 (크론이라 admin 밖)
require_once plugin_dir_path( __FILE__ ) . 'includes/parcels.php'; // 우체국 소포 발송 내역 → 배송비 지출 — 월말 결산이 부른다
require_once plugin_dir_path( __FILE__ ) . 'includes/cost.php'; // 상품 원가표 → 순이익 (옵션만 쓴다 · 월말 결산이 부른다)
require_once plugin_dir_path( __FILE__ ) . 'includes/monthly.php'; // 매출 → 월말 결산 (읽기 전용 · CSV · 디스코드 · 매달 1일 크론이라 admin 밖)
require_once plugin_dir_path( __FILE__ ) . 'includes/market.php'; // 도구 → 시장가 대조 (읽기 전용) — 경쟁 값을 크론이 받는다 (크론이라 admin 밖)
require_once plugin_dir_path( __FILE__ ) . 'includes/crm-report.php'; // CRM 기간 성과 — 고객 세그먼트 상자 + 열쇠로 닫힌 REST (집계만 · REST 라 admin 밖)
// 성과 분석(관리자 · 읽기 전용) — 리디자인 전후를 숫자로 견주기 위해.
// 보고서를 쓰고 나면 이 줄과 파일을 지운다.
if ( is_admin() ) {
require_once plugin_dir_path( __FILE__ ) . 'includes/impact.php';
// 노보 이벤트 — 기준 가격 대조 · 적용(관리자).
require_once plugin_dir_path( __FILE__ ) . 'includes/novo-admin.php';
// 매출 대시보드 — 이 가게의 진짜 주문 상태로 센다(관리자 · 읽기 전용).
require_once plugin_dir_path( __FILE__ ) . 'includes/sales.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/anatomy.php'; // 매출 → 주문 해부 (읽기 전용)
require_once plugin_dir_path( __FILE__ ) . 'includes/crm.php'; // 마케팅 → 고객 세그먼트 (읽기 전용 · CSV)
require_once plugin_dir_path( __FILE__ ) . 'includes/crm-sms.php'; // 고객 세그먼트 — SMS 수신 동의 · 문자 사이트 양식(.xls)
require_once plugin_dir_path( __FILE__ ) . 'includes/crm-plan.php'; // 고객 세그먼트 — 주차 플랜 · 보상 라벨 · 한 사람에 한 보상
require_once plugin_dir_path( __FILE__ ) . 'includes/crm-impact.php'; // 고객 세그먼트 — 발송 성과 (받은 사람이 샀는가)
require_once plugin_dir_path( __FILE__ ) . 'includes/crm-sends.php'; // 마케팅 → 문자 참여 — 보낸 파일 여러 개를 한 번에 (읽기 전용)
// 도구 → 코드 찾기 — 화면 문구가 테마 · 스니펫 어디서 나오는지 (읽기 전용).
require_once plugin_dir_path( __FILE__ ) . 'includes/finder.php';
// 마케팅 → 쿠폰 한 번에 만들기 — 손님마다 다른 금액의 쿠폰을 한 번에 만들고 문자 목록을 뽑는다.
require_once plugin_dir_path( __FILE__ ) . 'includes/coupon-admin.php';
// 마케팅 → 적립금 한 번에 넣기 — 엑셀 → 미리 보기 → 사장님이 「넣기」를 눌러야 들어간다 (잔액 + 원장 · 두 번 안 넣음 · 거두기).
require_once plugin_dir_path( __FILE__ ) . 'includes/points-admin.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/points-check.php'; // 도구 → 적립금 확인 — 손님 한 명의 계정 · 잔액 · 내역 (읽기 전용)
// 도구 → 분류 묶기 — 상품 이름을 붙여 넣으면 새 분류를 만들고 그 상품들을 넣는다.
require_once plugin_dir_path( __FILE__ ) . 'includes/cat-admin.php';
// 도구 → 옵션 이름 바꾸기 — 다른 상품들의 선택칸에 적힌 옵션 이름 · 값을 한 번에.
require_once plugin_dir_path( __FILE__ ) . 'includes/opt-admin.php';
// 도구 → 송장 진단 — 송장번호가 어느 주문 메타에 있는지 한 화면으로 가른다 (읽기 전용).
require_once plugin_dir_path( __FILE__ ) . 'includes/tracking-admin.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/orders-doctor.php'; // 도구 → 주문내역 진단 (읽기 전용)
// 도구 → 후기 적립 진단 — 사진 후기에 적립금이 실제로 나가는지 (읽기 전용).
// 한 번 확인되면 이 줄과 파일을 지운다.
require_once plugin_dir_path( __FILE__ ) . 'includes/review-doctor.php';
// 도구 → 금액 점검 — 옛 기준가로 결제된 주문을 찾는다 (읽기 전용).
require_once plugin_dir_path( __FILE__ ) . 'includes/price-check-admin.php';
// 도구 → 성인인증 점검 — 인증 판정이 걸리기 전에 가입한 회원을 센다 (읽기 전용)
require_once plugin_dir_path( __FILE__ ) . 'includes/verify-admin.php';
// 도구 → 입금 2차 판정 — 키플이 「입금자명 불일치」로 둔 문자를 끝 일치로 다시 판정해 보여 준다 (읽기 전용)
require_once plugin_dir_path( __FILE__ ) . 'includes/bank-second.php';
}
register_activation_hook( __FILE__, __NAMESPACE__ . '\\Pages\\ensure' );
/**
* 프론트엔드에 디자인 토큰을 불러옵니다.
*
* 지금 불러오는 CSS 는 :root 커스텀 속성만 정의하므로 화면은 바뀌지 않습니다.
* 배포 경로가 실제로 동작하는지 확인하는 것이 목적입니다. 실제 스타일은
* 디자인 방향이 확정된 뒤 별도 핸들로 추가하고 여기에 의존성으로 겁니다.
*/
function enqueue_tokens(): void {
$relative = 'assets/tokens.css';
$path = plugin_dir_path( __FILE__ ) . $relative;
if ( ! file_exists( $path ) ) {
return;
}
// 파일 수정 시각을 버전으로 써서 배포 직후 캐시가 남지 않게 합니다.
wp_enqueue_style(
'duckhoo-tokens',
plugin_dir_url( __FILE__ ) . $relative,
array(),
(string) filemtime( $path )
);
}
add_action( 'wp_enqueue_scripts', __NAMESPACE__ . '\\enqueue_tokens' );
/**
* 블록 에디터에서도 같은 토큰을 쓸 수 있게 합니다.
*
* 에디터와 프론트엔드가 다른 값을 쓰면 편집 화면에서 본 것과 실제 화면이
* 어긋나므로 같은 파일을 불러옵니다. enqueue_block_assets 는 프론트엔드에서도
* 발동해 같은 파일을 두 번 불러오므로 에디터 전용 훅을 씁니다.
*/
function enqueue_editor_tokens(): void {
$relative = 'assets/tokens.css';
$path = plugin_dir_path( __FILE__ ) . $relative;
if ( ! file_exists( $path ) ) {
return;
}
wp_enqueue_style(
'duckhoo-tokens-editor',
plugin_dir_url( __FILE__ ) . $relative,
array(),
(string) filemtime( $path )
);
}
add_action( 'enqueue_block_editor_assets', __NAMESPACE__ . '\\enqueue_editor_tokens' );
/**
* 목록 · 상세 · 장바구니 · 결제 · 계정 화면에 테마 CSS 를 얹습니다.
*
* 홈은 플러그인이 통째로 그리므로 빼고, 나머지 화면은 테마와 WooCommerce 템플릿
* 위에 assets/duckhoo-theme.css 를 얹습니다. 관리자 화면에 손으로 붙여넣을 필요가
* 없고, 플러그인을 끄면 그대로 돌아갑니다.
*/
function enqueue_theme_css(): void {
if ( is_admin() || is_front_page() || is_page( 'membership-cancel' ) ) {
return;
}
$relative = 'assets/duckhoo-theme.css';
$path = plugin_dir_path( __FILE__ ) . $relative;
if ( ! file_exists( $path ) ) {
return;
}
wp_enqueue_style( 'duckhoo-theme', plugin_dir_url( __FILE__ ) . $relative, array( 'duckhoo-tokens' ), (string) filemtime( $path ) );
}
add_action( 'wp_enqueue_scripts', __NAMESPACE__ . '\\enqueue_theme_css', 30 );
/**
* 회원탈퇴 화면 스타일을 불러옵니다.
*
* 그 화면에서만 씁니다. 상품 목록처럼 카드가 수십 개 깔리는 곳에 쓸데없는
* CSS 를 얹지 않기 위해서입니다.
*/
function enqueue_membership_cancel_style(): void {
$post = get_post();
if ( ! $post || 'membership-cancel' !== $post->post_name ) {
return;
}
$relative = 'assets/membership-cancel.css';
$path = plugin_dir_path( __FILE__ ) . $relative;
if ( ! file_exists( $path ) ) {
return;
}
wp_enqueue_style(
'duckhoo-membership-cancel',
plugin_dir_url( __FILE__ ) . $relative,
array( 'duckhoo-tokens' ),
(string) filemtime( $path )
);
}
add_action( 'wp_enqueue_scripts', __NAMESPACE__ . '\\enqueue_membership_cancel_style', 20 );
/**
* 고객이 직접 취소할 수 있는 주문 상태를 넓힙니다.
*
* WooCommerce 는 기본적으로 pending·failed 주문에만 "취소" 링크를 내보냅니다.
* 이 사이트의 무통장입금 주문은 on-hold("결제 확인 중")로 들어가는데 그 목록에
* 없어서 마이페이지에 취소 버튼 자체가 그려지지 않았습니다. 고객이 취소를 못 하던
* 원인이 여기입니다.
*
* 여는 것은 입금 전 상태(on-hold, 그리고 이름표가 "입금전"인 상태)뿐입니다.
* 입금확인(payment-confirmed) 이후는 열지 않습니다 — 환불이 남아 사람이 봐야 합니다.
*/
function customer_cancellable_statuses(): array {
static $slugs = null;
if ( null !== $slugs ) {
return $slugs;
}
$slugs = array();
if ( ! function_exists( 'wc_get_order_statuses' ) ) {
return $slugs;
}
// 실제 사이트(2026-09 관리자 주문 목록)에는 "입금전" 상태가 없다. 무통장입금 주문은
// WooCommerce 기본 on-hold("결제 확인 중")로 들어간다 — 293건. 그게 입금 전이다.
// WooCommerce 는 기본값으로 pending·failed 만 취소를 허용하므로 on-hold 를 더한다.
// keyple 이 나중에 "입금전" 이름표를 붙인 상태를 쓰면 그것도 잡는다.
$slugs[] = 'on-hold';
foreach ( wc_get_order_statuses() as $slug => $label ) {
if ( '입금전' === trim( wp_strip_all_tags( (string) $label ) ) ) {
$slugs[] = (string) preg_replace( '/^wc-/', '', (string) $slug );
}
}
return array_values( array_unique( $slugs ) );
}
/**
* 취소 가능 상태 목록에 입금전을 더합니다.
*
* 기존 목록은 지우지 않고 더하기만 합니다. WooCommerce 나 다른 플러그인이
* 넣어 둔 상태를 뺏지 않기 위해서입니다.
*
* @param array $statuses 취소 가능 상태 슬러그 목록.
* @return array
*/
function allow_cancel_before_deposit( $statuses ): array {
return array_values( array_unique( array_merge( (array) $statuses, customer_cancellable_statuses() ) ) );
}
add_filter( 'woocommerce_valid_order_statuses_for_cancel', __NAMESPACE__ . '\\allow_cancel_before_deposit' );