PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 1.95
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v1.95
2.1.0 2.0.15 2.0.12 2.0.10 2.0.4 2.0.1 2.0.0 1.95.3 1.95.2 1.95 1.91.6 trunk 1.11 1.12 1.13 1.20 1.21 1.22 1.23 1.30 1.31 1.32 1.35 1.40 1.41 All 42 releases
fluent-boards / app / Hooks / Handlers / ExternalPages.php

ExternalPages.php in FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration 1.95, at app/Hooks/Handlers/ExternalPages.php

175 lines 6.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBoards\App\Hooks\Handlers;
4
5 use FluentBoards\App\Models\Attachment;
6 use FluentBoards\App\App;
7 use FluentBoards\App\Models\CommentImage;
8 use FluentBoards\App\Models\Meta;
9 use FluentBoards\App\Models\Task;
10 use FluentBoards\App\Services\Constant;
11 use FluentBoards\App\Services\Libs\FileSystem;
12
13 class ExternalPages
14 {
15 public function view_uploaded_comment_image()
16 {
17 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public file serving endpoint, security validated via hash
18 $attachmentHash = isset($_REQUEST['fbs_comment_image']) ? sanitize_text_field(wp_unslash($_REQUEST['fbs_comment_image'])) : '';
19 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public file serving endpoint, security validated via hash
20 $boardId = isset($_REQUEST['fbs_bid']) ? sanitize_text_field(wp_unslash($_REQUEST['fbs_bid'])) : '';
21
22 if (empty($attachmentHash)) {
23 die(esc_html__('Invalid Attachment Hash', 'fluent-boards'));
24 }
25
26 $attachment = $this->getUploadedImageByHash($attachmentHash);
27
28 if (!$attachment) {
29 die(esc_html__('Invalid Attachment Hash', 'fluent-boards'));
30 }
31
32 if ('local' !== $attachment->driver) {
33 if(!empty($attachment->file_path)){
34 $this->redirectToExternalAttachment($attachment->full_url);
35 }else{
36 die(esc_html__('File could not be found', 'fluent-boards'));
37 }
38 return;
39 }
40 $fileName = $attachment->file_path;
41 $boardId = $boardId;
42 $filePath = $fileName;
43 if(!file_exists($fileName)){
44 $filePath = FileSystem::setSubDir('board_' . $boardId)->getDir() . DIRECTORY_SEPARATOR . $fileName;
45 }
46
47 if (!file_exists($filePath)) {
48 die(esc_html__('File could not be found.', 'fluent-boards'));
49 }
50
51 $this->serveLocalAttachment($attachment, $filePath);
52 }
53
54 public function view_comment_image()
55 {
56 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public file serving endpoint, security validated via hash and signature
57 $attachmentHash = isset($_REQUEST['fbs_comment_image']) ? sanitize_text_field(wp_unslash($_REQUEST['fbs_comment_image'])) : '';
58
59 if (empty($attachmentHash)) {
60 die(esc_html__('Invalid Attachment Hash', 'fluent-boards'));
61 }
62
63 $attachment = $this->getUploadedImageByHash($attachmentHash);
64
65 if (!$attachment) {
66 die(esc_html__('Invalid Attachment Hash', 'fluent-boards'));
67 }
68
69 if (in_array($attachment->object_type, [Constant::COMMENT_IMAGE])) {
70 $attachment->load('comment');
71 } elseif (in_array($attachment->object_type, [Constant::TASK_DESCRIPTION])) {
72 $attachment['task'] = Task::find($attachment->object_id);
73 }
74
75 // check signature hash
76 if (!$this->validateAttachmentSignature($attachment)) {
77 die(esc_html__('Sorry, Your secure sign is invalid, Please reload the previous page and get new signed url', 'fluent-boards'));
78 }
79
80 //If external file
81 if ('local' !== $attachment->driver) {
82 if(!empty($attachment->file_path)){
83 $this->redirectToExternalAttachment($attachment->full_url);
84 }else{
85 die(esc_html__('File could not be found', 'fluent-boards'));
86 }
87 }
88
89 //Handle Local file
90 if (in_array($attachment->object_type, [Constant::COMMENT_IMAGE])) {
91 $fileName = $attachment->file_path;
92 $boardId = $attachment->comment->board_id;
93 } elseif (in_array($attachment->object_type, [Constant::TASK_DESCRIPTION])) {
94 $fileName = $attachment->file_path;
95 $boardId = $attachment->task->board_id;
96 }
97
98 $filePath = $fileName;
99 if(!file_exists($fileName)){
100 $filePath = FileSystem::setSubDir('board_' . $boardId)->getDir() . DIRECTORY_SEPARATOR . $fileName;
101 }
102
103 if (!file_exists($filePath)) {
104 die(esc_html__('File could not be found.', 'fluent-boards'));
105 }
106
107 $this->serveLocalAttachment($attachment, $filePath);
108 }
109
110 private function getUploadedImageByHash($attachmentHash)
111 {
112 return CommentImage::where('file_hash', $attachmentHash)->first();
113 }
114
115 private function serveLocalAttachment($attachment, $filePath)
116 {
117 ob_get_clean();
118 header("Content-Type: {$attachment->attachment_type}");
119 header("Content-Disposition: inline; filename=\"{$attachment->title}\"");;
120 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile -- Serving binary file content directly to browser, WP_Filesystem not suitable for this use case
121 readfile($filePath);
122 die();
123 }
124
125 private function validateAttachmentSignature($attachment)
126 {
127 $sign = md5($attachment->id . gmdate('YmdH'));
128 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Signature validation serves as security mechanism
129 $requestSign = isset($_REQUEST['secure_sign']) ? sanitize_text_field(wp_unslash($_REQUEST['secure_sign'])) : '';
130 return $sign === $requestSign;
131 }
132
133 public function redirectToPage()
134 {
135 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public redirect endpoint, no sensitive operations
136 $taskId = isset($_GET['taskId']) ? absint(wp_unslash($_GET['taskId'])) : 0;
137
138 if (!$taskId) {
139 wp_die(esc_html__('Invalid task ID', 'fluent-boards'));
140 }
141
142 $task = Task::findOrFail($taskId);
143 if ($this->isFrontendEnabled() == 'no') {
144 $urlBase = apply_filters('fluent_boards/app_url', admin_url('admin.php?page=fluent-boards#/'));
145 $page_url = $urlBase . 'boards/' . $task->board_id . '/tasks/' . $task->id . '-' .substr($task->title, 0, 10);
146 wp_redirect($page_url);
147 exit;
148 } else {
149 $urlBase = apply_filters('fluent_boards/app_url');
150 $page_url = $urlBase . 'boards/' . $task->board_id . '/tasks/' . $task->id . '-' .substr($task->title, 0, 10);
151 wp_redirect($page_url);
152 exit;
153 }
154
155 die();
156 }
157
158 private function isFrontendEnabled()
159 {
160 $storedSettings = get_option('fluent_boards_modules', []);
161 $settings = is_string($storedSettings) ? maybe_unserialize($storedSettings) : $storedSettings;
162
163 if (is_array($settings) && isset($settings['frontend']['enabled'])) {
164 return $settings['frontend']['enabled'];
165 }
166
167 return 'no';
168 }
169 private function redirectToExternalAttachment($redirectUrl)
170 {
171 wp_redirect($redirectUrl, 307);
172 exit();
173 }
174 }
175