Skip to content

Commit 8ccdee4

Browse files
committed
JulianPrieber committed 1 hour ago Create global validation endpoint
Users can now overwrite global default validation rules by overwriting any value comconfig/validation.php changed to apply to v4.8.x application layout from: c5036b8
1 parent 7bd6cea commit 8ccdee4

11 files changed

Lines changed: 321 additions & 99 deletions

File tree

‎app/Functions/functions.php‎

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -199,59 +199,3 @@ function footer($key)
199199
} else {
200200
$title = env('TITLE_FOOTER_'.$upperStr);
201201
}
202-
return $title;
203-
}
204-
205-
function strip_tags_except_allowed_protocols($str) {
206-
preg_match_all('/<a[^>]+>(.*?)<\/a>/i', $str, $matches, PREG_SET_ORDER);
207-
208-
foreach ($matches as $val) {
209-
if (!preg_match('/href=["\'](http:|https:|mailto:|tel:)[^"\']*["\']/', $val[0])) {
210-
$str = str_replace($val[0], $val[1], $str);
211-
}
212-
}
213-
214-
return $str;
215-
}
216-
217-
if(!function_exists('setBlockAssetContext')) {
218-
function setBlockAssetContext($type = null) {
219-
static $currentType = null;
220-
if ($type !== null) {
221-
$currentType = $type;
222-
}
223-
return $currentType;
224-
}
225-
}
226-
227-
// Get custom block assets
228-
if(!function_exists('block_asset')) {
229-
function block_asset($file) {
230-
$type = setBlockAssetContext(); // Retrieve the current type context
231-
return url("block-asset/$type?asset=$file");
232-
}
233-
}
234-
235-
if(!function_exists('get_block_file_contents')) {
236-
function get_block_file_contents($file) {
237-
$type = setBlockAssetContext(); // Retrieve the current type context
238-
return file_get_contents(base_path("blocks/$type/$file"));
239-
}
240-
}
241-
242-
function block_text_translation_check($text) {
243-
if (empty($text)) {
244-
return false;
245-
}
246-
$translate = __("messages.$text");
247-
return $translate === "messages.$text" ? true : false;
248-
}
249-
250-
function block_text($text) {
251-
$translate = __("messages.$text");
252-
return $translate === "messages.$text" ? $text : $translate;
253-
}
254-
255-
function bt($text) {
256-
return block_text($text);
257-
}

‎app/Functions/validation.php‎

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
<?php
2+
3+
if (!function_exists('validation_image_extensions')) {
4+
function validation_image_extensions(): array
5+
{
6+
return array_values(array_filter((array) config('validation.image_extensions', ['jpeg', 'jpg', 'png', 'webp', 'gif', 'svg'])));
7+
}
8+
}
9+
10+
if (!function_exists('validation_image_accept')) {
11+
function validation_image_accept(): string
12+
{
13+
return implode(',', array_map(static fn ($extension) => '.' . $extension, validation_image_extensions()));
14+
}
15+
}
16+
17+
if (!function_exists('validation_image_label')) {
18+
function validation_image_label(): string
19+
{
20+
return implode(', ', array_map(static fn ($extension) => strtoupper($extension), validation_image_extensions()));
21+
}
22+
}
23+
24+
if (!function_exists('validation_rich_text_allowed_tags')) {
25+
function validation_rich_text_allowed_tags(): array
26+
{
27+
return array_values(array_filter((array) config('validation.rich_text.allowed_tags', ['a', 'p', 'strong', 'i', 'ul', 'ol', 'li', 'blockquote', 'h2', 'h3', 'h4', 'br'])));
28+
}
29+
}
30+
31+
if (!function_exists('validation_rich_text_allowed_protocols')) {
32+
function validation_rich_text_allowed_protocols(): array
33+
{
34+
return array_values(array_filter((array) config('validation.rich_text.allowed_protocols', ['http', 'https', 'mailto', 'tel'])));
35+
}
36+
}
37+
38+
if (!function_exists('sanitize_rich_text')) {
39+
function sanitize_rich_text($html): string
40+
{
41+
$html = (string) $html;
42+
43+
if ($html === '') {
44+
return '';
45+
}
46+
47+
$allowedTags = validation_rich_text_allowed_tags();
48+
$allowedProtocols = validation_rich_text_allowed_protocols();
49+
50+
if (!class_exists('DOMDocument')) {
51+
return strip_tags($html, '<' . implode('><', $allowedTags) . '>');
52+
}
53+
54+
$dom = new \DOMDocument('1.0', 'UTF-8');
55+
$previousUseErrors = libxml_use_internal_errors(true);
56+
$wrapperId = 'linkstack-rich-text-root';
57+
$dom->loadHTML('<?xml encoding="UTF-8"><div id="' . $wrapperId . '">' . $html . '</div>', LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
58+
libxml_clear_errors();
59+
libxml_use_internal_errors($previousUseErrors);
60+
61+
$wrapper = $dom->getElementById($wrapperId);
62+
if (!$wrapper) {
63+
return strip_tags($html, '<' . implode('><', $allowedTags) . '>');
64+
}
65+
66+
return sanitize_rich_text_node($wrapper, $allowedTags, $allowedProtocols);
67+
}
68+
}
69+
70+
if (!function_exists('sanitize_rich_text_node')) {
71+
function sanitize_rich_text_node(\DOMNode $node, array $allowedTags, array $allowedProtocols): string
72+
{
73+
$output = '';
74+
75+
foreach ($node->childNodes as $childNode) {
76+
if ($childNode->nodeType === XML_TEXT_NODE) {
77+
$output .= htmlspecialchars($childNode->nodeValue ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
78+
continue;
79+
}
80+
81+
if ($childNode->nodeType !== XML_ELEMENT_NODE) {
82+
continue;
83+
}
84+
85+
$tagName = strtolower($childNode->nodeName);
86+
if (!in_array($tagName, $allowedTags, true)) {
87+
if (in_array($tagName, ['script', 'style'], true)) {
88+
continue;
89+
}
90+
91+
$output .= sanitize_rich_text_node($childNode, $allowedTags, $allowedProtocols);
92+
continue;
93+
}
94+
95+
if ($tagName === 'br') {
96+
$output .= '<br>';
97+
continue;
98+
}
99+
100+
$attributes = '';
101+
102+
if ($tagName === 'a') {
103+
$href = trim((string) $childNode->attributes?->getNamedItem('href')?->nodeValue);
104+
$isAllowedLink = false;
105+
106+
if ($href !== '') {
107+
$scheme = strtolower((string) parse_url($href, PHP_URL_SCHEME));
108+
if (in_array($scheme, $allowedProtocols, true)) {
109+
$isAllowedLink = true;
110+
$attributes .= ' href="' . htmlspecialchars($href, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '"';
111+
}
112+
}
113+
114+
if (!$isAllowedLink) {
115+
$output .= sanitize_rich_text_node($childNode, $allowedTags, $allowedProtocols);
116+
continue;
117+
}
118+
119+
$attributes .= ' rel="noopener noreferrer nofollow"';
120+
}
121+
122+
$output .= '<' . $tagName . $attributes . '>';
123+
$output .= sanitize_rich_text_node($childNode, $allowedTags, $allowedProtocols);
124+
$output .= '</' . $tagName . '>';
125+
}
126+
127+
return $output;
128+
}
129+
}
130+
131+
if (!function_exists('strip_tags_except_allowed_protocols')) {
132+
function strip_tags_except_allowed_protocols($str)
133+
{
134+
return sanitize_rich_text($str);
135+
}
136+
}
137+
138+
if (!function_exists('validation_image_extensions')) {
139+
function validation_image_extensions(): array
140+
{
141+
return array_values(array_filter((array) config('validation.image_extensions', ['jpeg', 'jpg', 'png', 'webp', 'gif', 'svg'])));
142+
}
143+
}
144+
145+
if (!function_exists('validation_image_accept')) {
146+
function validation_image_accept(): string
147+
{
148+
return implode(',', array_map(static fn ($extension) => '.' . $extension, validation_image_extensions()));
149+
}
150+
}
151+
152+
if (!function_exists('validation_image_label')) {
153+
function validation_image_label(): string
154+
{
155+
return implode(', ', array_map(static fn ($extension) => strtoupper($extension), validation_image_extensions()));
156+
}
157+
}
158+
159+
if (!function_exists('validation_rich_text_allowed_tags')) {
160+
function validation_rich_text_allowed_tags(): array
161+
{
162+
return array_values(array_filter((array) config('validation.rich_text.allowed_tags', ['a', 'p', 'strong', 'i', 'ul', 'ol', 'li', 'blockquote', 'h2', 'h3', 'h4', 'br'])));
163+
}
164+
}
165+
166+
if (!function_exists('validation_rich_text_allowed_protocols')) {
167+
function validation_rich_text_allowed_protocols(): array
168+
{
169+
return array_values(array_filter((array) config('validation.rich_text.allowed_protocols', ['http', 'https', 'mailto', 'tel'])));
170+
}
171+
}
172+
173+
if (!function_exists('sanitize_rich_text')) {
174+
function sanitize_rich_text($html): string
175+
{
176+
$html = (string) $html;
177+
178+
if ($html === '') {
179+
return '';
180+
}
181+
182+
$allowedTags = validation_rich_text_allowed_tags();
183+
$allowedProtocols = validation_rich_text_allowed_protocols();
184+
185+
if (!class_exists('DOMDocument')) {
186+
return strip_tags($html, '<' . implode('><', $allowedTags) . '>');
187+
}
188+
189+
$dom = new \DOMDocument('1.0', 'UTF-8');
190+
$previousUseErrors = libxml_use_internal_errors(true);
191+
$wrapperId = 'linkstack-rich-text-root';
192+
$dom->loadHTML('<?xml encoding="UTF-8"><div id="' . $wrapperId . '">' . $html . '</div>', LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
193+
libxml_clear_errors();
194+
libxml_use_internal_errors($previousUseErrors);
195+
196+
$wrapper = $dom->getElementById($wrapperId);
197+
if (!$wrapper) {
198+
return strip_tags($html, '<' . implode('><', $allowedTags) . '>');
199+
}
200+
201+
return sanitize_rich_text_node($wrapper, $allowedTags, $allowedProtocols);
202+
}
203+
}
204+
205+
if (!function_exists('sanitize_rich_text_node')) {
206+
function sanitize_rich_text_node(\DOMNode $node, array $allowedTags, array $allowedProtocols): string
207+
{
208+
$output = '';
209+
210+
foreach ($node->childNodes as $childNode) {
211+
if ($childNode->nodeType === XML_TEXT_NODE) {
212+
$output .= htmlspecialchars($childNode->nodeValue ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
213+
continue;
214+
}
215+
216+
if ($childNode->nodeType !== XML_ELEMENT_NODE) {
217+
continue;
218+
}
219+
220+
$tagName = strtolower($childNode->nodeName);
221+
if (!in_array($tagName, $allowedTags, true)) {
222+
if (in_array($tagName, ['script', 'style'], true)) {
223+
continue;
224+
}
225+
226+
$output .= sanitize_rich_text_node($childNode, $allowedTags, $allowedProtocols);
227+
continue;
228+
}
229+
230+
if ($tagName === 'br') {
231+
$output .= '<br>';
232+
continue;
233+
}
234+
235+
$attributes = '';
236+
237+
if ($tagName === 'a') {
238+
$href = trim((string) $childNode->attributes?->getNamedItem('href')?->nodeValue);
239+
$isAllowedLink = false;
240+
241+
if ($href !== '') {
242+
$scheme = strtolower((string) parse_url($href, PHP_URL_SCHEME));
243+
if (in_array($scheme, $allowedProtocols, true)) {
244+
$isAllowedLink = true;
245+
$attributes .= ' href="' . htmlspecialchars($href, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '"';
246+
}
247+
}
248+
249+
if (!$isAllowedLink) {
250+
$output .= sanitize_rich_text_node($childNode, $allowedTags, $allowedProtocols);
251+
continue;
252+
}
253+
254+
$attributes .= ' rel="noopener noreferrer nofollow"';
255+
}
256+
257+
$output .= '<' . $tagName . $attributes . '>';
258+
$output .= sanitize_rich_text_node($childNode, $allowedTags, $allowedProtocols);
259+
$output .= '</' . $tagName . '>';
260+
}
261+
262+
return $output;
263+
}
264+
}
265+
266+
if (!function_exists('strip_tags_except_allowed_protocols')) {
267+
function strip_tags_except_allowed_protocols($str)
268+
{
269+
return sanitize_rich_text($str);
270+
}
271+
}

‎app/Http/Controllers/AdminController.php‎

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -322,10 +322,12 @@ public function deleteLinkUser(request $request)
322322
public function editUser(request $request)
323323
{
324324
$request->validate([
325-
"name" => "",
326-
"email" => "",
327-
"password" => "",
328-
"littlelink_name" => "",
325+
"name" => "sometimes|string|max:255",
326+
"email" => "sometimes|email|max:255",
327+
"password" => "sometimes|string|min:8",
328+
"littlelink_name" => "sometimes|string|max:255",
329+
"image" => 'sometimes|file|mimes:' . implode(',', validation_image_extensions()) . '|max:2048',
330+
"background" => 'sometimes|file|mimes:' . implode(',', validation_image_extensions()) . '|max:2048',
329331
]);
330332

331333
$id = $request->id;
@@ -334,7 +336,7 @@ public function editUser(request $request)
334336
$password = Hash::make($request->password);
335337
$profilePhoto = $request->file("image");
336338
$littlelink_name = $request->littlelink_name;
337-
$littlelink_description = $request->littlelink_description;
339+
$littlelink_description = sanitize_rich_text($request->littlelink_description);
338340
$role = $request->role;
339341
$customBackground = $request->file("background");
340342
$theme = $request->theme;
@@ -366,7 +368,10 @@ public function editUser(request $request)
366368
]);
367369
}
368370
if (!empty($profilePhoto)) {
369-
$profilePhoto->move(base_path("assets/img"), $id . "_" . time() . ".png");
371+
$profilePhoto->move(
372+
base_path("assets/img"),
373+
$id . "_" . time() . "." . $profilePhoto->extension(),
374+
);
370375
}
371376
if (!empty($customBackground)) {
372377
$directory = base_path("assets/img/background-img/");
@@ -434,6 +439,11 @@ public function editSite(request $request)
434439
$logo = $request->file("image");
435440
$icon = $request->file("icon");
436441

442+
$request->validate([
443+
"image" => 'sometimes|file|mimes:' . implode(',', validation_image_extensions()) . '|max:2048',
444+
"icon" => 'sometimes|file|mimes:' . implode(',', validation_image_extensions()) . '|max:2048',
445+
]);
446+
437447
Page::first()->update(["home_message" => $message]);
438448

439449
if (!empty($logo)) {

0 commit comments

Comments
 (0)