EventController.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. <?php
  2. namespace App\Controllers\Api;
  3. use CodeIgniter\HTTP\ResponseInterface;
  4. class EventController extends BaseApiController
  5. {
  6. /**
  7. * Get event list
  8. */
  9. public function index()
  10. {
  11. $auth = $this->requireAuth();
  12. if ($auth instanceof ResponseInterface) {
  13. return $auth;
  14. }
  15. $params = $this->getPaginationParams();
  16. $builder = $this->getDB()->table('events');
  17. // Search
  18. $searchType = $this->request->getGet('search_type');
  19. $searchKeyword = $this->request->getGet('search_keyword');
  20. if ($searchType && $searchKeyword) {
  21. if ($searchType === 'title') {
  22. $builder->like('title', $searchKeyword);
  23. } elseif ($searchType === 'name') {
  24. $builder->like('name', $searchKeyword);
  25. } elseif ($searchType === 'content') {
  26. $builder->like('content', $searchKeyword);
  27. }
  28. }
  29. $builder->orderBy('is_notice', 'DESC');
  30. $builder->orderBy('id', 'DESC');
  31. $result = $this->paginatedResponse($builder, $params);
  32. return $this->respondSuccess($result);
  33. }
  34. /**
  35. * Get single event
  36. */
  37. public function show($id = null)
  38. {
  39. $auth = $this->requireAuth();
  40. if ($auth instanceof ResponseInterface) {
  41. return $auth;
  42. }
  43. $builder = $this->getDB()->table('events');
  44. $event = $builder->where('id', $id)->get()->getRow();
  45. if (!$event) {
  46. return $this->respondError('이벤트를 찾을 수 없습니다.', ResponseInterface::HTTP_NOT_FOUND);
  47. }
  48. // Parse file_urls JSON
  49. $event->file_urls = $this->normalizeFileUrls($event->file_urls ?? '[]');
  50. // Fix image paths in content: /event/image.jpg -> /uploads/bbs/event/image.jpg
  51. if (!empty($event->content)) {
  52. // src="/event/ 형태를 src="/uploads/bbs/event/ 로 변경
  53. $event->content = str_replace('src="/event/', 'src="/uploads/bbs/event/', $event->content);
  54. // src='/event/ 형태도 처리
  55. $event->content = str_replace("src='/event/", "src='/uploads/bbs/event/", $event->content);
  56. // YouTube iframe 경로 수정: /embed/ID -> https://www.youtube.com/embed/ID
  57. $event->content = str_replace('src="/embed/', 'src="https://www.youtube.com/embed/', $event->content);
  58. $event->content = str_replace("src='/embed/", "src='https://www.youtube.com/embed/", $event->content);
  59. // 도메인 추가: src="/uploads -> src="http://도메인/uploads
  60. // 단, 이미 http:// 또는 https://로 시작하는 URL은 제외
  61. $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https://' : 'http://';
  62. $currentDomain = $protocol . ($_SERVER['HTTP_HOST'] ?? 'localhost');
  63. // 정규표현식으로 /uploads로 시작하고 앞에 http(s)://가 없는 경우만 도메인 추가
  64. $event->content = preg_replace(
  65. '/src="(?!https?:\/\/)\/uploads\//',
  66. 'src="' . $currentDomain . '/uploads/',
  67. $event->content
  68. );
  69. $event->content = preg_replace(
  70. "/src='(?!https?:\/\/)\/uploads\//",
  71. "src='" . $currentDomain . "/uploads/",
  72. $event->content
  73. );
  74. }
  75. // Increment view count
  76. $builder->where('id', $id)->set('views', 'views + 1', false)->update();
  77. return $this->respondSuccess($event);
  78. }
  79. /**
  80. * Create event
  81. */
  82. public function create()
  83. {
  84. $auth = $this->requireAuth();
  85. if ($auth instanceof ResponseInterface) {
  86. return $auth;
  87. }
  88. $json = $this->request->getJSON();
  89. $data = [
  90. 'site' => $json->site ?? 'common',
  91. 'category' => $json->category ?? '',
  92. 'allow_comment' => isset($json->allow_comment) ? (int)$json->allow_comment : 0,
  93. 'is_notice' => isset($json->is_notice) ? (int)$json->is_notice : 0,
  94. 'name' => $json->name ?? '',
  95. 'email' => $json->email ?? '',
  96. 'start_date' => $json->start_date ?? '',
  97. 'end_date' => $json->end_date ?? '',
  98. 'title' => $json->title ?? '',
  99. 'content' => $json->content ?? '',
  100. 'file_urls' => json_encode($json->file_urls ?? []),
  101. 'views' => 0,
  102. 'created_at' => date('Y-m-d H:i:s')
  103. ];
  104. $builder = $this->getDB()->table('events');
  105. $builder->insert($data);
  106. return $this->respondSuccess(['id' => $this->getDB()->insertID()], '이벤트가 등록되었습니다.');
  107. }
  108. /**
  109. * Update event
  110. */
  111. public function update($id = null)
  112. {
  113. $auth = $this->requireAuth();
  114. if ($auth instanceof ResponseInterface) {
  115. return $auth;
  116. }
  117. $json = $this->request->getJSON();
  118. $data = [
  119. 'site' => $json->site ?? 'common',
  120. 'category' => $json->category ?? '',
  121. 'allow_comment' => isset($json->allow_comment) ? (int)$json->allow_comment : 0,
  122. 'is_notice' => isset($json->is_notice) ? (int)$json->is_notice : 0,
  123. 'name' => $json->name ?? '',
  124. 'email' => $json->email ?? '',
  125. 'start_date' => $json->start_date ?? '',
  126. 'end_date' => $json->end_date ?? '',
  127. 'title' => $json->title ?? '',
  128. 'content' => $json->content ?? '',
  129. 'file_urls' => json_encode($json->file_urls ?? []),
  130. 'updated_at' => date('Y-m-d H:i:s')
  131. ];
  132. $builder = $this->getDB()->table('events');
  133. $builder->where('id', $id)->update($data);
  134. return $this->respondSuccess(null, '이벤트가 수정되었습니다.');
  135. }
  136. /**
  137. * Delete event
  138. */
  139. public function delete($id = null)
  140. {
  141. $auth = $this->requireAuth();
  142. if ($auth instanceof ResponseInterface) {
  143. return $auth;
  144. }
  145. $builder = $this->getDB()->table('events');
  146. $builder->where('id', $id)->delete();
  147. return $this->respondSuccess(null, '이벤트가 삭제되었습니다.');
  148. }
  149. /**
  150. * Normalize file_urls to always return object array
  151. * Handles both old format (string array) and new format (object array)
  152. */
  153. private function normalizeFileUrls($fileUrlsJson)
  154. {
  155. $fileUrls = json_decode($fileUrlsJson ?? '[]');
  156. if (empty($fileUrls) || !is_array($fileUrls)) {
  157. return [];
  158. }
  159. $normalized = [];
  160. foreach ($fileUrls as $item) {
  161. // If already an object with url property, keep it
  162. if (is_object($item) && isset($item->url)) {
  163. $normalized[] = $item;
  164. }
  165. // If it's a string (old format), convert to object
  166. elseif (is_string($item)) {
  167. $filename = basename($item);
  168. $normalized[] = (object)[
  169. 'name' => $filename,
  170. 'url' => $item,
  171. 'size' => 0 // Size unknown for migrated data
  172. ];
  173. }
  174. }
  175. return $normalized;
  176. }
  177. /**
  178. * Get public event list (no auth required)
  179. */
  180. public function publicList()
  181. {
  182. $params = $this->getPaginationParams();
  183. $builder = $this->getDB()->table('events');
  184. // Filter by site (ford or lincoln) + common
  185. $site = $this->request->getGet('site');
  186. if ($site && in_array($site, ['ford', 'lincoln'])) {
  187. $builder->where("(site = '{$site}' OR site = 'common')");
  188. }
  189. // Show all events (including notices)
  190. $builder->orderBy('id', 'DESC');
  191. $result = $this->paginatedResponse($builder, $params);
  192. // Parse file_urls for each item
  193. if (!empty($result['items'])) {
  194. foreach ($result['items'] as &$item) {
  195. $item->file_urls = $this->normalizeFileUrls($item->file_urls ?? '[]');
  196. }
  197. }
  198. return $this->respondSuccess($result);
  199. }
  200. /**
  201. * Get public single event (no auth required)
  202. */
  203. public function publicShow($id = null)
  204. {
  205. $builder = $this->getDB()->table('events');
  206. $event = $builder->where('id', $id)->get()->getRow();
  207. if (!$event) {
  208. return $this->respondError('이벤트를 찾을 수 없습니다.', ResponseInterface::HTTP_NOT_FOUND);
  209. }
  210. // Parse file_urls JSON
  211. $event->file_urls = $this->normalizeFileUrls($event->file_urls ?? '[]');
  212. // Fix image paths in content: /event/image.jpg -> /uploads/bbs/event/image.jpg
  213. if (!empty($event->content)) {
  214. // src="/event/ 형태를 src="/uploads/bbs/event/ 로 변경
  215. $event->content = str_replace('src="/event/', 'src="/uploads/bbs/event/', $event->content);
  216. // src='/event/ 형태도 처리
  217. $event->content = str_replace("src='/event/", "src='/uploads/bbs/event/", $event->content);
  218. // YouTube iframe 경로 수정: /embed/ID -> https://www.youtube.com/embed/ID
  219. $event->content = str_replace('src="/embed/', 'src="https://www.youtube.com/embed/', $event->content);
  220. $event->content = str_replace("src='/embed/", "src='https://www.youtube.com/embed/", $event->content);
  221. // 도메인 추가: src="/uploads -> src="http://도메인/uploads
  222. // 단, 이미 http:// 또는 https://로 시작하는 URL은 제외
  223. $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https://' : 'http://';
  224. $currentDomain = $protocol . ($_SERVER['HTTP_HOST'] ?? 'localhost');
  225. // 정규표현식으로 /uploads로 시작하고 앞에 http(s)://가 없는 경우만 도메인 추가
  226. $event->content = preg_replace(
  227. '/src="(?!https?:\/\/)\/uploads\//',
  228. 'src="' . $currentDomain . '/uploads/',
  229. $event->content
  230. );
  231. $event->content = preg_replace(
  232. "/src='(?!https?:\/\/)\/uploads\//",
  233. "src='" . $currentDomain . "/uploads/",
  234. $event->content
  235. );
  236. }
  237. // Increment view count
  238. $builder->where('id', $id)->set('views', 'views + 1', false)->update();
  239. return $this->respondSuccess($event);
  240. }
  241. }