PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 2.2.0
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v2.2.0
2.2.0 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 All 43 releases
← All changes | app/Http/Controllers/WebhookController.php +260 -9 1.31 → 2.2.0 View file →
@@ -2,8 +2,10 @@
2 2
3 3 namespace FluentBoards\App\Http\Controllers;
4 4
5 5 use FluentBoards\App\Models\Webhook;
6 +use FluentBoards\App\Models\Meta;
7 +use FluentBoards\App\Models\Relation;
6 8 use FluentBoards\Framework\Http\Request\Request;
7 9
8 10
9 11 class WebhookController extends Controller
@@ -10,12 +12,11 @@
10 12 {
11 13 public function index(Request $request, Webhook $webhook)
12 14 {
13 15 $fields = $webhook->getFields();
14 - $search = $request->getSafe('search', '');
16 + $search = $request->getSafe('search', 'sanitize_text_field', '');
15 17
16 18 $webhooks = $webhook->latest()->get()->toArray();
17 -
18 19 if ( ! empty($search)) {
19 20 $search = strtolower($search);
20 21 $webhooks = array_map(function ($row) use ($search) {
21 22 $name = strtolower($row['value']['name']);
@@ -44,15 +45,24 @@
44 45 }
45 46
46 47 public function create(Request $request, Webhook $webhook)
47 48 {
48 - $webhook = $webhook->store(
49 - $this->validate(
50 - $request->all(),
51 - ['name' => 'required']
52 - )
53 - );
49 + $name = $request->name;
50 + $boardId = $request->getSafe('board', 'intval');
51 + $stageId = $request->getSafe('stage', 'intval');
52 + // Sanitize name field
53 + $name = sanitize_text_field($name);
54 54
55 + if(!$name || !$boardId || !$stageId) {
56 + return $this->sendError('Name, board and stage are required');
57 + }
58 +
59 + $webhook = $webhook->store([
60 + 'name' => $name,
61 + 'board' => $boardId,
62 + 'stage' => $stageId,
63 + ]);
64 +
55 65 return [
56 66 'id' => $webhook->id,
57 67 'webhook' => $webhook->value,
58 68 'webhooks' => $webhook->latest()->get(),
@@ -61,10 +71,22 @@
61 71 }
62 72
63 73 public function update(Request $request, Webhook $webhook, $id)
64 74 {
65 - $webhook->find($id)->saveChanges($request->all());
75 + $id = absint($id);
76 + $data = $request->only(['name']);
77 + // Sanitize data if needed based on webhook structure
78 + if (isset($data['name'])) {
79 + $data['name'] = sanitize_text_field($data['name']);
80 + }
66 81
82 + $foundWebhook = $webhook->find($id);
83 + if (!$foundWebhook) {
84 + return $this->sendError(__('Webhook not found', 'fluent-boards'), 404);
85 + }
86 +
87 + $foundWebhook->saveChanges($data);
88 +
67 89 return [
68 90 'webhooks' => $webhook->latest()->get(),
69 91 'message' => __('Successfully updated the webhook', 'fluent-boards'),
70 92 ];
@@ -71,12 +93,241 @@
71 93 }
72 94
73 95 public function delete(Webhook $webhook, $id)
74 96 {
97 + $id = absint($id);
75 98 $webhook->where('id', $id)->delete();
76 99
77 100 return [
78 101 'webhooks' => $webhook->latest()->get(),
79 102 'message' => __('Successfully deleted the webhook', 'fluent-boards'),
103 + ];
104 + }
105 +
106 + // Outgoing Webhook Methods
107 + public function outgoingWebhooks(Request $request, Meta $meta)
108 + {
109 + $search = $request->getSafe('search', 'sanitize_text_field', '');
110 + $boardId = $request->getSafe('board_id', 'intval');
111 +
112 + $webhooks = $meta->where('object_type', 'outgoing_webhook')
113 + ->latest()
114 + ->get()
115 + ->toArray();
116 +
117 + // Add board filtering logic
118 + if ($boardId) {
119 + $webhooks = array_filter($webhooks, function($row) use ($boardId) {
120 + if (!isset($row['value']['board_id'])) {
121 + return false;
122 + }
123 +
124 + $webhookBoardId = $row['value']['board_id'];
125 +
126 + // Handle array of board IDs
127 + if (is_array($webhookBoardId)) {
128 + return in_array($boardId, array_map('intval', $webhookBoardId));
129 + }
130 +
131 + // Handle single board ID or null (all boards)
132 + return $webhookBoardId === null || intval($webhookBoardId) === $boardId;
133 + });
134 + }
135 +
136 + if (!empty($search)) {
137 + $search = strtolower($search);
138 + $webhooks = array_map(function ($row) use ($search) {
139 + $name = strtolower($row['value']['name']);
140 + if ($row['value'] && str_contains($name, $search)) {
141 + return $row;
142 + }
143 + return null;
144 + }, $webhooks);
145 + }
146 +
147 + $rows = [];
148 + foreach ($webhooks as $row) {
149 + if ($row) {
150 + $rows[] = $row;
151 + }
152 + }
153 +
154 + return [
155 + 'webhooks' => $rows
156 + ];
157 + }
158 +
159 + public function createOutgoingWebhook(Request $request)
160 + {
161 + $data = $this->validate(
162 + $request->only(['name', 'url', 'board_id', 'triggered_events', 'event_mode', 'header_type', 'headers']),
163 + [
164 + 'name' => 'required',
165 + 'url' => 'required',
166 + 'board_id' => 'array',
167 + 'triggered_events' => 'array',
168 + ]
169 + );
170 +
171 + $data['name'] = sanitize_text_field($data['name']);
172 + $data['url'] = sanitize_url($data['url']);
173 +
174 + // Sanitize header_type if present
175 + if (isset($data['header_type'])) {
176 + $data['header_type'] = sanitize_text_field($data['header_type']);
177 + }
178 +
179 + if(isset($data['header_type']) && $data['header_type'] == 'with_headers') {
180 + // sanitize all headers key and value
181 + if (!empty($data['headers']) && is_array($data['headers'])) {
182 + foreach ($data['headers'] as $index => $header) {
183 + if (isset($header['name'])) {
184 + $data['headers'][$index]['name'] = sanitize_text_field($header['name']);
185 + }
186 + if (isset($header['value'])) {
187 + $data['headers'][$index]['value'] = sanitize_text_field($header['value']);
188 + }
189 + }
190 + }
191 + }
192 +
193 + $data['event_mode'] = ($data['event_mode'] ?? 'selected') === 'all' ? 'all' : 'selected';
194 + $data['triggered_events'] = array_values(array_filter(array_map('sanitize_text_field', $data['triggered_events'] ?? [])));
195 + if ($data['event_mode'] !== 'all' && !$data['triggered_events']) {
196 + return $this->sendError(__('At least one event must be selected for the webhook to be triggered.', 'fluent-boards'));
197 + }
198 +
199 + // Sanitize board_id array to integers
200 + if (!empty($data['board_id']) && is_array($data['board_id'])) {
201 + $data['board_id'] = array_filter(array_map('intval', $data['board_id']));
202 + } else {
203 + $data['board_id'] = [];
204 + }
205 +
206 + $webhook = Meta::create([
207 + 'object_type' => 'outgoing_webhook',
208 + 'value' => $data
209 + ]);
210 +
211 + foreach ($data['board_id'] as $boardId) {
212 + $boardId = absint($boardId);
213 + Relation::create([
214 + 'object_id' => $webhook->id,
215 + 'object_type' => 'outgoing_webhook_board',
216 + 'foreign_id' => $boardId,
217 + 'settings' => \maybe_serialize($data['triggered_events'] ?? []),
218 + 'preferences' => ['event_mode' => $data['event_mode']]
219 + ]);
220 + }
221 +
222 +
223 + return [
224 + 'id' => $webhook->id,
225 + 'webhook' => $webhook->value,
226 + 'webhooks' => Meta::where('object_type', 'outgoing_webhook')->latest()->get(),
227 + 'message' => __('Successfully Created the Outgoing WebHook', 'fluent-boards'),
228 + ];
229 + }
230 +
231 + public function updateOutgoingWebhook(Request $request, $id)
232 + {
233 + $id = absint($id);
234 + $data = $this->validate(
235 + $request->all(),
236 + [
237 + 'name' => 'required',
238 + 'url' => 'required',
239 + 'board_id' => 'array',
240 + 'triggered_events' => 'array',
241 + ]
242 + );
243 +
244 + $data['name'] = sanitize_text_field($data['name']);
245 + $data['url'] = sanitize_url($data['url']);
246 +
247 + // Sanitize header_type if present
248 + if (isset($data['header_type'])) {
249 + $data['header_type'] = sanitize_text_field($data['header_type']);
250 + }
251 +
252 + if(isset($data['header_type']) && $data['header_type'] == 'with_headers') {
253 + // sanitize all headers key and value
254 + if (!empty($data['headers']) && is_array($data['headers'])) {
255 + foreach ($data['headers'] as $index => $header) {
256 + if (isset($header['name'])) {
257 + $data['headers'][$index]['name'] = sanitize_text_field($header['name']);
258 + }
259 + if (isset($header['value'])) {
260 + $data['headers'][$index]['value'] = sanitize_text_field($header['value']);
261 + }
262 + }
263 + }
264 + }
265 +
266 + // Requests from legacy clients remain explicit subscriptions.
267 + $data['event_mode'] = ($data['event_mode'] ?? 'selected') === 'all' ? 'all' : 'selected';
268 + $data['triggered_events'] = array_values(array_filter(array_map('sanitize_text_field', $data['triggered_events'] ?? [])));
269 +
270 + // Sanitize board_id array to integers
271 + if (!empty($data['board_id']) && is_array($data['board_id'])) {
272 + $data['board_id'] = array_filter(array_map('intval', $data['board_id']));
273 + } else {
274 + $data['board_id'] = [];
275 + }
276 +
277 + $webhook = Meta::find($id);
278 +
279 + if (!$webhook || $webhook->object_type !== 'outgoing_webhook') {
280 + return $this->sendError('Outgoing webhook not found.', 404);
281 + }
282 +
283 + $webhook->value = $data;
284 + $webhook->save();
285 +
286 +
287 + // Update relations
288 + Relation::where('object_type', 'outgoing_webhook_board')
289 + ->where('object_id', $webhook->id)
290 + ->delete();
291 +
292 + // Recreate relations
293 + foreach ($data['board_id'] as $boardId) {
294 + $boardId = absint($boardId);
295 + Relation::create([
296 + 'object_id' => $webhook->id,
297 + 'object_type' => 'outgoing_webhook_board',
298 + 'foreign_id' => $boardId,
299 + 'settings' => \maybe_serialize($data['triggered_events'] ?? []),
300 + 'preferences' => ['event_mode' => $data['event_mode']]
301 + ]);
302 + }
303 +
304 +
305 + return [
306 + 'webhooks' => Meta::where('object_type', 'outgoing_webhook')->latest()->get(),
307 + 'message' => __('Successfully updated the outgoing webhook', 'fluent-boards'),
308 + ];
309 + }
310 +
311 + public function deleteOutgoingWebhook($id)
312 + {
313 + $id = absint($id);
314 + $webhook = Meta::find($id);
315 +
316 + if (!$webhook || $webhook->object_type !== 'outgoing_webhook') {
317 + return $this->sendError('Outgoing webhook not found.', 404);
318 + }
319 +
320 + // Delete associated relations
321 + Relation::where('object_type', 'outgoing_webhook_board')
322 + ->where('object_id', $webhook->id)
323 + ->delete();
324 +
325 +
326 + $webhook->delete();
327 +
328 + return [
329 + 'webhooks' => Meta::where('object_type', 'outgoing_webhook')->latest()->get(),
330 + 'message' => __('Successfully deleted the outgoing webhook', 'fluent-boards'),
80 331 ];
81 332 }
82 333 }