PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 2.1.0
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v2.1.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 1.41 All 42 releases
← All changes | app/Functions/helpers.php +195 -1 1.202.1.0 View file →
@@ -18,11 +18,54 @@
18 18 return is_null($key) ? $api : $api->{$key};
19 19 }
20 20 }
21 21
22 +if (!function_exists('fluent_boards_sanitize_description')) {
23 + function fluent_boards_sanitize_description($description): string
24 + {
25 + $description = preg_replace_callback('/<((?:https?:\/\/)[^<>\s]+)>/i', function ($matches) {
26 + $url = esc_url_raw(html_entity_decode($matches[1], ENT_QUOTES | ENT_HTML5, 'UTF-8'));
27 +
28 + return $url ? '[' . $url . '](' . str_replace(')', '%29', $url) . ')' : $matches[0];
29 + }, (string) $description);
30 +
31 + // KSES encodes standalone > characters, which breaks Markdown quotes.
32 + // Protect only quote prefixes; all HTML still passes through KSES.
33 + $quotePrefix = 'fbs-quote-' . wp_generate_uuid4() . '-';
34 + $quotes = [];
35 + $description = preg_replace_callback('/^[ \t]*(?:>[ \t]*)+/m', function ($matches) use (&$quotes, $quotePrefix) {
36 + $key = $quotePrefix . count($quotes);
37 + $quotes[$key] = $matches[0];
38 + return $key;
39 + }, $description);
40 +
41 + return strtr(wp_kses_post($description), $quotes);
42 + }
43 +}
44 +
22 45 if (!function_exists('fluent_boards_user_avatar')) {
23 46 function fluent_boards_user_avatar($email, $name = '')
24 47 {
48 + $user = get_user_by('email', $email);
49 +
50 + if ($user) {
51 + $photo_url = get_user_meta($user->ID, 'fbs_profile_photo', true);
52 + if ($photo_url) {
53 + return apply_filters('fluent_boards/get_avatar', $photo_url, $email);
54 + }
55 +
56 + $has_custom_avatar = get_user_meta($user->ID, 'wp_user_avatar', true);
57 + if ($has_custom_avatar) {
58 + $custom_avatar_attachment = get_post($has_custom_avatar);
59 + if ($custom_avatar_attachment) {
60 + $avatar_url = wp_get_attachment_url($has_custom_avatar);
61 + return apply_filters('fluent_boards/get_avatar', $avatar_url, $email);
62 + }
63 + }
64 + $avatar_url = get_avatar_url($user->ID, array('size' => 128));
65 + return apply_filters('fluent_boards/get_avatar', $avatar_url, $email);
66 + }
67 +
25 68 $hash = md5(strtolower(trim($email)));
26 69 /**
27 70 * Gravatar URL by Email
28 71 *
@@ -62,8 +105,20 @@
62 105 return apply_filters('fluent_boards/app_url', admin_url('admin.php?page=fluent-boards#/'));
63 106 }
64 107 }
65 108
109 +if (!function_exists('fluent_boards_is_rtl')) {
110 + /**
111 + * Determine if Fluent Boards should render right-to-left UI.
112 + *
113 + * @return bool
114 + */
115 + function fluent_boards_is_rtl(): bool
116 + {
117 + return is_rtl();
118 + }
119 +}
120 +
66 121 function fluent_boards_get_pref_settings($cached = true)
67 122 {
68 123 static $pref = null;
69 124
@@ -85,9 +140,14 @@
85 140 ],
86 141 'menu_settings' => [
87 142 'in_fluent_crm' => 'no',
88 143 'menu_position' => 3
89 - ]
144 + ],
145 + 'recurring_task' => [
146 + 'enabled' => 'no',
147 + 'all_boards' => 'yes',
148 + 'selected_boards' => []
149 + ],
90 150 ];
91 151
92 152 $storedSettings = get_option('fluent_boards_modules', []);
93 153 if ($storedSettings && is_array($storedSettings)) {
@@ -144,5 +204,139 @@
144 204 ]);
145 205 }
146 206
147 207 return $exit;
208 +}
209 +
210 +
211 +function fluentBoardsDb()
212 +{
213 + return fluentBoards('db');
214 +}
215 +
216 +/**
217 + * Retrieves features configuration.
218 + *
219 + * @deprecated 1.90 Use fluent_boards_get_features_config() instead.
220 + */
221 +function fbsGetFeaturesConfig()
222 +{
223 + _deprecated_function(__FUNCTION__, '1.90', 'fluent_boards_get_features_config');
224 + return fluent_boards_get_features_config();
225 +}
226 +
227 +function fluent_boards_get_features_config()
228 +{
229 + $features = fluent_boards_get_option('_fbs_features', []);
230 +
231 + $defaults = [
232 + 'cloud_storage' => 'no',
233 + // more advanced features/config can be added here
234 + ];
235 +
236 + if (defined('FLUENT_BOARDS_CLOUD_STORAGE') && FLUENT_BOARDS_CLOUD_STORAGE) {
237 + $features['cloud_storage'] = 'yes';
238 + }
239 +
240 + return wp_parse_args($features, $defaults);
241 +}
242 +
243 +/**
244 + * Updates an option value.
245 + *
246 + * @deprecated 1.90 Use fluent_boards_get_option() instead.
247 + */
248 +function fbsGetOption($key, $default = null)
249 +{
250 + _deprecated_function(__FUNCTION__, '1.90', 'fluent_boards_get_option');
251 + return fluent_boards_get_option($key, $default);
252 +}
253 +
254 +/**
255 + * Updates an option value.
256 + *
257 + * @deprecated 1.90 Use fluent_boards_update_option() instead.
258 + */
259 +function fbsUpdateOption($key, $value)
260 +{
261 + _deprecated_function(__FUNCTION__, '1.90', 'fluent_boards_update_option');
262 + return fluent_boards_update_option($key, $value);
263 +}
264 +
265 +
266 +function fluentboardsCsvMimes()
267 +{
268 + /**
269 + * Board Import CSV Mimes
270 + *
271 + * @return array array of CSV mimes
272 + */
273 + return apply_filters('fluent_boards_csv_mimes', [
274 + 'text/csv',
275 + 'text/plain',
276 + 'application/csv',
277 + 'text/comma-separated-values',
278 + 'application/excel',
279 + 'application/vnd.ms-excel',
280 + 'application/vnd.msexcel',
281 + 'text/anytext',
282 + 'application/octet-stream',
283 + 'application/txt'
284 + ]);
285 +}
286 +
287 +function fluent_boards_string_to_bool($value)
288 +{
289 + // Normalize known string values to booleans
290 + $trueFalseMap = [
291 + 'yes' => true,
292 + 'no' => false,
293 + 'true' => true,
294 + 'false' => false,
295 + '1' => true,
296 + '0' => false,
297 + true => true,
298 + false => false,
299 + ];
300 +
301 + // Allow modification of the map
302 + $trueFalseMap = apply_filters('fluent_boards/true_false_convert', $trueFalseMap);
303 +
304 + // Handle array input recursively
305 + if (is_array($value)) {
306 + return array_map('fluent_boards_string_to_bool', $value);
307 + }
308 +
309 + // Normalize string input
310 + $key = is_string($value) ? strtolower(trim($value)) : $value;
311 +
312 + // Match normalized value against map
313 + if (array_key_exists($key, $trueFalseMap)) {
314 + return $trueFalseMap[$key];
315 + }
316 +
317 + // Fallback: return original value if not recognized
318 + return $value;
319 +}
320 +
321 +/**
322 + * Builds the canonical Fluent Boards Upgrade-to-Pro URL.
323 + *
324 + * @param string $utmContent Exact in-plugin CTA placement.
325 + * @return string
326 + */
327 +function fluent_boards_get_upgrade_url($utmContent = '')
328 +{
329 + $queryArgs = [
330 + 'utm_source' => 'fluent-boards',
331 + 'utm_medium' => 'free_plugin',
332 + 'utm_campaign' => 'upgrade_pro',
333 + 'utm_term' => FLUENT_BOARDS_PLUGIN_VERSION,
334 + ];
335 +
336 + $utmContent = sanitize_key($utmContent);
337 + if ($utmContent) {
338 + $queryArgs['utm_content'] = $utmContent;
339 + }
340 +
341 + return add_query_arg($queryArgs, 'https://fluentboards.com/pricing/');
148 342 }