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
fluent-boards / app / Functions / helpers.php

helpers.php in FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration 2.2.0, at app/Functions/helpers.php

355 lines 9.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use FluentBoards\App\App;
4 use FluentBoards\App\Services\Constant;
5
6 // $app is available
7
8 if (!function_exists('fluentBoards')) {
9 function fluentBoards($module = null)
10 {
11 return App::getInstance($module);
12 }
13 }
14
15 if (!function_exists('FluentBoardsApi')) {
16 function FluentBoardsApi($key = null)
17 {
18 $api = fluentBoards('api');
19 return is_null($key) ? $api : $api->{$key};
20 }
21 }
22
23 if (!function_exists('fluent_boards_sanitize_description')) {
24 function fluent_boards_sanitize_description($description): string
25 {
26 $description = preg_replace_callback('/<((?:https?:\/\/)[^<>\s]+)>/i', function ($matches) {
27 $url = esc_url_raw(html_entity_decode($matches[1], ENT_QUOTES | ENT_HTML5, 'UTF-8'));
28
29 return $url ? '[' . $url . '](' . str_replace(')', '%29', $url) . ')' : $matches[0];
30 }, (string) $description);
31
32 // Markdown email autolinks otherwise look like disallowed HTML to KSES.
33 $description = preg_replace_callback('/<(?:mailto:)?([^<>\s]+@[^<>\s]+)>/i', function ($matches) {
34 $email = html_entity_decode($matches[1], ENT_QUOTES | ENT_HTML5, 'UTF-8');
35 if (!is_email($email)) {
36 return $matches[0];
37 }
38
39 $label = str_replace(['*', '_', '`'], ['\\*', '\\_', '\\`'], $email);
40 return '[' . $label . '](mailto:' . $email . ')';
41 }, $description);
42
43 // KSES encodes standalone > characters, which breaks Markdown quotes.
44 // Protect only quote prefixes; all HTML still passes through KSES.
45 $quotePrefix = 'fbs-quote-' . wp_generate_uuid4() . '-';
46 $quotes = [];
47 $description = preg_replace_callback('/^[ \t]*(?:>[ \t]*)+/m', function ($matches) use (&$quotes, $quotePrefix) {
48 $key = $quotePrefix . count($quotes);
49 $quotes[$key] = $matches[0];
50 return $key;
51 }, $description);
52
53 return strtr(wp_kses_post($description), $quotes);
54 }
55 }
56
57 if (!function_exists('fluent_boards_user_avatar')) {
58 function fluent_boards_user_avatar($email, $name = '')
59 {
60 $user = get_user_by('email', $email);
61
62 if ($user) {
63 $photo_url = get_user_meta($user->ID, Constant::PROFILE_PHOTO_META_KEY, true);
64 if ($photo_url) {
65 return apply_filters('fluent_boards/get_avatar', $photo_url, $email);
66 }
67
68 $has_custom_avatar = get_user_meta($user->ID, 'wp_user_avatar', true);
69 if ($has_custom_avatar) {
70 $custom_avatar_attachment = get_post($has_custom_avatar);
71 if ($custom_avatar_attachment) {
72 $avatar_url = wp_get_attachment_url($has_custom_avatar);
73 return apply_filters('fluent_boards/get_avatar', $avatar_url, $email);
74 }
75 }
76 $avatar_url = get_avatar_url($user->ID, array('size' => 128));
77 return apply_filters('fluent_boards/get_avatar', $avatar_url, $email);
78 }
79
80 $hash = md5(strtolower(trim($email)));
81 /**
82 * Gravatar URL by Email
83 *
84 * @return string $gravatar url of the gravatar image
85 */
86 $fallback = '';
87 if ($name) {
88 $fallback = '&d=https%3A%2F%2Fui-avatars.com%2Fapi%2F' . urlencode($name) . '/128';
89 }
90
91 return apply_filters('fluent_boards/get_avatar',
92 "https://www.gravatar.com/avatar/{$hash}?s=128" . $fallback,
93 $email
94 );
95 }
96 }
97
98 if (!function_exists('fluent_boards_mix')) {
99 function fluent_boards_mix($path, $manifestDirectory = '')
100 {
101 return fluentBoards('url.assets') . ltrim($path, '/');
102 }
103 }
104
105 if (!function_exists('FluentBoardsAssetUrl')) {
106 function FluentBoardsAssetUrl($path = null)
107 {
108 $assetUrl = fluentBoards('url.assets');
109
110 return $path ? ($assetUrl . $path) : $assetUrl;
111 }
112 }
113
114 if (!function_exists('fluent_boards_page_url')) {
115 function fluent_boards_page_url(): ?string
116 {
117 return apply_filters('fluent_boards/app_url', admin_url('admin.php?page=fluent-boards#/'));
118 }
119 }
120
121 if (!function_exists('fluent_boards_is_rtl')) {
122 /**
123 * Determine if Fluent Boards should render right-to-left UI.
124 *
125 * @return bool
126 */
127 function fluent_boards_is_rtl(): bool
128 {
129 return is_rtl();
130 }
131 }
132
133 function fluent_boards_get_pref_settings($cached = true)
134 {
135 static $pref = null;
136
137 if ($cached && $pref) {
138 return $pref;
139 }
140
141 $settings = [
142 'timeTracking' => [
143 'enabled' => 'no',
144 'all_boards' => 'yes',
145 'selected_boards' => []
146 ],
147 'frontend' => [
148 'enabled' => 'no',
149 'slug' => 'projects',
150 'render_type' => 'standalone',
151 'page_id' => ''
152 ],
153 'menu_settings' => [
154 'in_fluent_crm' => 'no',
155 'menu_position' => 3
156 ],
157 'recurring_task' => [
158 'enabled' => 'no',
159 'all_boards' => 'yes',
160 'selected_boards' => []
161 ],
162 ];
163
164 $storedSettings = get_option('fluent_boards_modules', []);
165 if ($storedSettings && is_array($storedSettings)) {
166 $settings = wp_parse_args($storedSettings, $settings);
167 }
168
169 $pref = $settings;
170
171 return $settings;
172 }
173
174 if (!function_exists('fluent_boards_site_logo')) {
175 function fluent_boards_site_logo()
176 {
177 $logo_url = '';
178 if (function_exists('get_custom_logo') && has_custom_logo()) {
179 $custom_logo_id = get_theme_mod('custom_logo');
180 $logo = wp_get_attachment_image_src($custom_logo_id, 'full');
181 if ($logo) {
182 $logo_url = $logo[0];
183 }
184 }
185 return apply_filters('fluent_boards/site_logo', $logo_url);
186 }
187 }
188
189 function fluent_boards_get_option($key, $default = null)
190 {
191 $exit = \FluentBoards\App\Models\Meta::where('object_type', 'option')
192 ->where('key', $key)
193 ->first();
194
195 if ($exit) {
196 return $exit->value;
197 }
198
199 return $default;
200 }
201
202 function fluent_boards_update_option($key, $value)
203 {
204 $exit = \FluentBoards\App\Models\Meta::where('object_type', 'option')
205 ->where('key', $key)
206 ->first();
207
208 if ($exit) {
209 $exit->value = $value;
210 $exit->save();
211 } else {
212 $exit = \FluentBoards\App\Models\Meta::create([
213 'object_type' => 'option',
214 'key' => $key,
215 'value' => $value
216 ]);
217 }
218
219 return $exit;
220 }
221
222
223 function fluentBoardsDb()
224 {
225 return fluentBoards('db');
226 }
227
228 /**
229 * Retrieves features configuration.
230 *
231 * @deprecated 1.90 Use fluent_boards_get_features_config() instead.
232 */
233 function fbsGetFeaturesConfig()
234 {
235 _deprecated_function(__FUNCTION__, '1.90', 'fluent_boards_get_features_config');
236 return fluent_boards_get_features_config();
237 }
238
239 function fluent_boards_get_features_config()
240 {
241 $features = fluent_boards_get_option('_fbs_features', []);
242
243 $defaults = [
244 'cloud_storage' => 'no',
245 // more advanced features/config can be added here
246 ];
247
248 if (defined('FLUENT_BOARDS_CLOUD_STORAGE') && FLUENT_BOARDS_CLOUD_STORAGE) {
249 $features['cloud_storage'] = 'yes';
250 }
251
252 return wp_parse_args($features, $defaults);
253 }
254
255 /**
256 * Updates an option value.
257 *
258 * @deprecated 1.90 Use fluent_boards_get_option() instead.
259 */
260 function fbsGetOption($key, $default = null)
261 {
262 _deprecated_function(__FUNCTION__, '1.90', 'fluent_boards_get_option');
263 return fluent_boards_get_option($key, $default);
264 }
265
266 /**
267 * Updates an option value.
268 *
269 * @deprecated 1.90 Use fluent_boards_update_option() instead.
270 */
271 function fbsUpdateOption($key, $value)
272 {
273 _deprecated_function(__FUNCTION__, '1.90', 'fluent_boards_update_option');
274 return fluent_boards_update_option($key, $value);
275 }
276
277
278 function fluentboardsCsvMimes()
279 {
280 /**
281 * Board Import CSV Mimes
282 *
283 * @return array array of CSV mimes
284 */
285 return apply_filters('fluent_boards_csv_mimes', [
286 'text/csv',
287 'text/plain',
288 'application/csv',
289 'text/comma-separated-values',
290 'application/excel',
291 'application/vnd.ms-excel',
292 'application/vnd.msexcel',
293 'text/anytext',
294 'application/octet-stream',
295 'application/txt'
296 ]);
297 }
298
299 function fluent_boards_string_to_bool($value)
300 {
301 // Normalize known string values to booleans
302 $trueFalseMap = [
303 'yes' => true,
304 'no' => false,
305 'true' => true,
306 'false' => false,
307 '1' => true,
308 '0' => false,
309 true => true,
310 false => false,
311 ];
312
313 // Allow modification of the map
314 $trueFalseMap = apply_filters('fluent_boards/true_false_convert', $trueFalseMap);
315
316 // Handle array input recursively
317 if (is_array($value)) {
318 return array_map('fluent_boards_string_to_bool', $value);
319 }
320
321 // Normalize string input
322 $key = is_string($value) ? strtolower(trim($value)) : $value;
323
324 // Match normalized value against map
325 if (array_key_exists($key, $trueFalseMap)) {
326 return $trueFalseMap[$key];
327 }
328
329 // Fallback: return original value if not recognized
330 return $value;
331 }
332
333 /**
334 * Builds the canonical Fluent Boards Upgrade-to-Pro URL.
335 *
336 * @param string $utmContent Exact in-plugin CTA placement.
337 * @return string
338 */
339 function fluent_boards_get_upgrade_url($utmContent = '')
340 {
341 $queryArgs = [
342 'utm_source' => 'fluent-boards',
343 'utm_medium' => 'free_plugin',
344 'utm_campaign' => 'upgrade_pro',
345 'utm_term' => FLUENT_BOARDS_PLUGIN_VERSION,
346 ];
347
348 $utmContent = sanitize_key($utmContent);
349 if ($utmContent) {
350 $queryArgs['utm_content'] = $utmContent;
351 }
352
353 return add_query_arg($queryArgs, 'https://fluentboards.com/pricing/');
354 }
355